Compare commits
	
		
			689 Commits
		
	
	
		
			3.10.3
			...
			gnome-3-12
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					1e047af492 | ||
| 
						 | 
					0546819e8f | ||
| 
						 | 
					62a6519db6 | ||
| 
						 | 
					a63821a57f | ||
| 
						 | 
					b7d2191059 | ||
| 
						 | 
					68f5674e62 | ||
| 
						 | 
					ffa8ff730e | ||
| 
						 | 
					80f9d1ff5d | ||
| 
						 | 
					547005b3a2 | ||
| 
						 | 
					36f051044c | ||
| 
						 | 
					0d75416d80 | ||
| 
						 | 
					c96f95ab90 | ||
| 
						 | 
					5ccbd3a821 | ||
| 
						 | 
					c9bd1d79cc | ||
| 
						 | 
					07e6219704 | ||
| 
						 | 
					8625d5b0cc | ||
| 
						 | 
					e021ca2135 | ||
| 
						 | 
					ba6a2cae9c | ||
| 
						 | 
					5852c17f76 | ||
| 
						 | 
					4fe0353991 | ||
| 
						 | 
					3c4a898a91 | ||
| 
						 | 
					0006feec67 | ||
| 
						 | 
					dd2ace2e37 | ||
| 
						 | 
					0327bd0a9d | ||
| 
						 | 
					42619cdd3b | ||
| 
						 | 
					76ea1f4dca | ||
| 
						 | 
					e4e3252ffc | ||
| 
						 | 
					e9ae77186d | ||
| 
						 | 
					a0c146bbfd | ||
| 
						 | 
					6d679148b6 | ||
| 
						 | 
					07198c5890 | ||
| 
						 | 
					4720bc3412 | ||
| 
						 | 
					7113821964 | ||
| 
						 | 
					c2ae98209d | ||
| 
						 | 
					21f0e422c0 | ||
| 
						 | 
					eb0f2b8b34 | ||
| 
						 | 
					65e781a973 | ||
| 
						 | 
					4590903159 | ||
| 
						 | 
					8b6fcbea50 | ||
| 
						 | 
					c6067ffa1d | ||
| 
						 | 
					7b4254da4e | ||
| 
						 | 
					2f576e3076 | ||
| 
						 | 
					60d7c3ad86 | ||
| 
						 | 
					43fdc15ac0 | ||
| 
						 | 
					f082a58071 | ||
| 
						 | 
					ae5dfb5740 | ||
| 
						 | 
					e8fdba4f66 | ||
| 
						 | 
					1030cdfe8a | ||
| 
						 | 
					ca2d626b59 | ||
| 
						 | 
					2b4eaf6b3f | ||
| 
						 | 
					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 | 
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -43,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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										41
									
								
								COPYING
									
									
									
									
									
								
							
							
						
						
									
										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.
 | 
			
		||||
 
 | 
			
		||||
@@ -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
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										274
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										274
									
								
								NEWS
									
									
									
									
									
								
							@@ -1,3 +1,277 @@
 | 
			
		||||
3.12.2
 | 
			
		||||
======
 | 
			
		||||
* Fix turning off airplane mode [Giovanni; #728512]
 | 
			
		||||
* Handle empty VPN keyfiles [Adel; #728681]
 | 
			
		||||
* Fix setting zero-level in osdWindow [Bastien; #727384]
 | 
			
		||||
* Fix removal of multiple workspace thumbnails at once [Florian; #728820]
 | 
			
		||||
* Make airplane mode menu insensitive in lock screen [Giovanni; #729224]
 | 
			
		||||
* Fix keynav for alternatives in AltSwitcher [Florian; #727259]
 | 
			
		||||
* Fix zombie search providers showing up [Jasper; #728597]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Giovanni Campagna, Adel Gadllah, Florian Müllner, Bastien Nocera,
 | 
			
		||||
  Jasper St. Pierre
 | 
			
		||||
 | 
			
		||||
Translations:
 | 
			
		||||
  Wouter Bolsterlee [nl], Daniel Korostil [uk], Ihar Hrachyshka [be],
 | 
			
		||||
  Giovanni Campagna [it], Carles Ferrando [ca@valencia]
 | 
			
		||||
 | 
			
		||||
3.12.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]
 | 
			
		||||
* Fix extension-prefs tool when linked with --as-needed [Florian; #727948]
 | 
			
		||||
* Don't always extend struts to the screen edge [Florian; #663690]
 | 
			
		||||
 | 
			
		||||
Contributors:
 | 
			
		||||
  Zeeshan Ali (Khattak), Cosimo Cecchi, Piotr Drąg, Rui Matos, Simon McVittie,
 | 
			
		||||
  Florian Müllner, Alejandro Piñeiro, Carlos Soriano
 | 
			
		||||
 | 
			
		||||
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],
 | 
			
		||||
  Мирослав Николић [sr, sr@latin]
 | 
			
		||||
 | 
			
		||||
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]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										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`
 | 
			
		||||
 
 | 
			
		||||
@@ -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");
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										106
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										106
									
								
								configure.ac
									
									
									
									
									
								
							@@ -1,5 +1,5 @@
 | 
			
		||||
AC_PREREQ(2.63)
 | 
			
		||||
AC_INIT([gnome-shell],[3.10.1],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell])
 | 
			
		||||
AC_INIT([gnome-shell],[3.12.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])
 | 
			
		||||
@@ -57,10 +58,25 @@ 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.38.1
 | 
			
		||||
MUTTER_MIN_VERSION=3.10.1
 | 
			
		||||
GJS_MIN_VERSION=1.39.0
 | 
			
		||||
MUTTER_MIN_VERSION=3.12.1
 | 
			
		||||
GTK_MIN_VERSION=3.7.9
 | 
			
		||||
GIO_MIN_VERSION=2.37.0
 | 
			
		||||
LIBECAL_MIN_VERSION=3.5.3
 | 
			
		||||
@@ -70,7 +86,6 @@ 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
 | 
			
		||||
 | 
			
		||||
@@ -80,7 +95,6 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
 | 
			
		||||
            gtk+-3.0 >= $GTK_MIN_VERSION
 | 
			
		||||
            atk-bridge-2.0
 | 
			
		||||
            gjs-internals-1.0 >= $GJS_MIN_VERSION
 | 
			
		||||
            libgnome-menu-3.0 >= $GNOME_MENUS_REQUIRED_VERSION
 | 
			
		||||
            $recorder_modules
 | 
			
		||||
            gdk-x11-3.0 libsoup-2.4
 | 
			
		||||
            xtst
 | 
			
		||||
@@ -91,9 +105,10 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION
 | 
			
		||||
            libcanberra libcanberra-gtk3
 | 
			
		||||
            telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION
 | 
			
		||||
            polkit-agent-1 >= $POLKIT_MIN_VERSION
 | 
			
		||||
            libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION
 | 
			
		||||
            libnm-gtk >= $NETWORKMANAGER_MIN_VERSION
 | 
			
		||||
            libsecret-unstable gcr-base-3 >= $GCR_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)
 | 
			
		||||
@@ -109,25 +124,25 @@ 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)
 | 
			
		||||
@@ -147,6 +162,9 @@ 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])
 | 
			
		||||
@@ -162,6 +180,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()
 | 
			
		||||
 | 
			
		||||
@@ -203,3 +253,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,6 +1,3 @@
 | 
			
		||||
wandadir = $(pkgdatadir)
 | 
			
		||||
dist_wanda_DATA = wanda.png
 | 
			
		||||
 | 
			
		||||
desktopdir=$(datadir)/applications
 | 
			
		||||
desktop_DATA = gnome-shell.desktop gnome-shell-extension-prefs.desktop
 | 
			
		||||
if HAVE_MUTTER_WAYLAND
 | 
			
		||||
@@ -42,6 +39,7 @@ 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			\
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@
 | 
			
		||||
Type=Application
 | 
			
		||||
_Name=GNOME Shell (wayland compositor)
 | 
			
		||||
_Comment=Window management and application launching
 | 
			
		||||
Exec=@bindir@/mutter-launch -- gnome-shell-wayland --wayland
 | 
			
		||||
Exec=@bindir@/mutter-launch -- gnome-shell-wayland --wayland --display-server
 | 
			
		||||
X-GNOME-Bugzilla-Bugzilla=GNOME
 | 
			
		||||
X-GNOME-Bugzilla-Product=gnome-shell
 | 
			
		||||
X-GNOME-Bugzilla-Component=general
 | 
			
		||||
 
 | 
			
		||||
@@ -13,12 +13,21 @@
 | 
			
		||||
    </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">
 | 
			
		||||
@@ -29,14 +38,6 @@
 | 
			
		||||
        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>
 | 
			
		||||
    <key name="app-picker-view" type="u">
 | 
			
		||||
      <default>0</default>
 | 
			
		||||
      <summary>App Picker View</summary>
 | 
			
		||||
@@ -54,10 +55,10 @@
 | 
			
		||||
    </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">
 | 
			
		||||
@@ -73,6 +74,7 @@
 | 
			
		||||
    <child name="calendar" schema="org.gnome.shell.calendar"/>
 | 
			
		||||
    <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/"
 | 
			
		||||
@@ -124,6 +126,11 @@
 | 
			
		||||
        Keybinding to focus the active notification.
 | 
			
		||||
      </_description>
 | 
			
		||||
    </key>
 | 
			
		||||
    <key name="pause-resume-tweens" type="as">
 | 
			
		||||
      <default>[]</default>
 | 
			
		||||
      <summary>Keybinding that pauses and resumes all running tweens, for debugging purposes</summary>
 | 
			
		||||
      <description></description>
 | 
			
		||||
    </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
  <schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/"
 | 
			
		||||
@@ -137,6 +144,32 @@
 | 
			
		||||
    </key>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
  <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="max-accuracy-level" enum="org.gnome.shell.geoclue.AccuracyLevel">
 | 
			
		||||
      <default>'exact'</default>
 | 
			
		||||
      <_summary>The maximum accuracy level of location.</_summary>
 | 
			
		||||
      <_description>
 | 
			
		||||
        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>
 | 
			
		||||
  </schema>
 | 
			
		||||
 | 
			
		||||
  <schema id="org.gnome.shell.app-switcher"
 | 
			
		||||
          path="/org/gnome/shell/app-switcher/"
 | 
			
		||||
          gettext-domain="@GETTEXT_PACKAGE@">
 | 
			
		||||
 
 | 
			
		||||
@@ -157,8 +157,9 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    min-width: 200px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.unicode-arrow {
 | 
			
		||||
    font-size: 120%;
 | 
			
		||||
.popup-menu-arrow {
 | 
			
		||||
    width: 16px;
 | 
			
		||||
    height: 16px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.popup-submenu-menu-item:open {
 | 
			
		||||
@@ -289,6 +290,20 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    spacing: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.nm-dialog-airplane-box {
 | 
			
		||||
    spacing: 12px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.nm-dialog-airplane-headline {
 | 
			
		||||
    font-size: 1.1em;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.nm-dialog-airplane-text {
 | 
			
		||||
    color: #999999;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.nm-dialog-header-icon {
 | 
			
		||||
    icon-size: 32px;
 | 
			
		||||
}
 | 
			
		||||
@@ -306,16 +321,13 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    font-size: 12pt;
 | 
			
		||||
    border-bottom: 1px solid #666;
 | 
			
		||||
    padding: 12px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.nm-dialog-item:checked {
 | 
			
		||||
    background-color: #333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.nm-dialog-item-box {
 | 
			
		||||
    spacing: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.nm-dialog-item:selected {
 | 
			
		||||
    background-color: #333;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.nm-dialog-icons {
 | 
			
		||||
    spacing: .5em;
 | 
			
		||||
}
 | 
			
		||||
@@ -658,7 +670,7 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    color: #e6e6e6;
 | 
			
		||||
    border-radius: 32px; /* wish we could do 50% */
 | 
			
		||||
    padding: 13px;
 | 
			
		||||
    border: 1px solid #5f5f5f; /* using rgba() is flaky unfortunately */
 | 
			
		||||
    border: 2px solid #5f5f5f; /* using rgba() is flaky unfortunately */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.system-menu-action:hover,
 | 
			
		||||
@@ -666,7 +678,7 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    color: white;
 | 
			
		||||
    background-color: #4c4c4c;
 | 
			
		||||
    border: none;
 | 
			
		||||
    padding: 14px;
 | 
			
		||||
    padding: 15px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.system-menu-action:active {
 | 
			
		||||
@@ -967,6 +979,8 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
 | 
			
		||||
.app-folder-icon {
 | 
			
		||||
    padding: 5px;
 | 
			
		||||
    spacing-rows: 5px;
 | 
			
		||||
    spacing-columns: 5px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.dash-item-container > StButton {
 | 
			
		||||
@@ -1292,12 +1306,18 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
    color: #eeeeec;
 | 
			
		||||
    border-radius: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.datemenu-date-label:hover,
 | 
			
		||||
.datemenu-date-label:focus {
 | 
			
		||||
    background-color: #999999;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.datemenu-date-label:active {
 | 
			
		||||
    background-color: #aaaaaa;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.calendar-day-base {
 | 
			
		||||
    font-size: 9pt;
 | 
			
		||||
    text-align: center;
 | 
			
		||||
@@ -1456,6 +1476,10 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    color: #999999;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.no-networks-box {
 | 
			
		||||
    spacing: 12px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.notification {
 | 
			
		||||
    border-radius: 10px 10px 0px 0px;
 | 
			
		||||
    background: rgba(0,0,0,0.9);
 | 
			
		||||
@@ -1633,8 +1657,8 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    color: #888888;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.chat-group-sent, .chat-group-meta {
 | 
			
		||||
    padding: 8px 0;
 | 
			
		||||
.chat-empty-line {
 | 
			
		||||
    font-size: 4px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.chat-received {
 | 
			
		||||
@@ -1659,6 +1683,7 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
.chat-meta-message {
 | 
			
		||||
    padding-left: 4px;
 | 
			
		||||
    font-size: 9pt;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    color: #bbbbbb;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1860,6 +1885,27 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    border-radius: 8px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Tile previews */
 | 
			
		||||
.tile-preview {
 | 
			
		||||
    background-color: rgba(74, 144, 217, 0.35);
 | 
			
		||||
    border: 1px solid #4a90d9; /* Adwaita selected bg color */
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-preview-left.on-primary {
 | 
			
		||||
    /* keep in sync with -panel-corner-radius */
 | 
			
		||||
    border-radius: 6px 0 0 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-preview-right.on-primary {
 | 
			
		||||
    /* keep in sync with -panel-corner-radius */
 | 
			
		||||
    border-radius: 0 6px 0 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.tile-preview-left.tile-preview-right.on-primary {
 | 
			
		||||
    /* keep in sync with -panel-corner-radius */
 | 
			
		||||
    border-radius: 6px 6px 0 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Modal Dialogs */
 | 
			
		||||
 | 
			
		||||
/* Dialog Subject Text Style */
 | 
			
		||||
@@ -1930,50 +1976,72 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    padding-top: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.end-session-dialog-subject {
 | 
			
		||||
.end-session-dialog-layout {
 | 
			
		||||
    padding-left: 17px;
 | 
			
		||||
    padding-bottom: 20px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.end-session-dialog-subject:rtl {
 | 
			
		||||
    padding-left: 0px;
 | 
			
		||||
.end-session-dialog-layout:rtl {
 | 
			
		||||
    padding-right: 17px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.end-session-dialog-description {
 | 
			
		||||
    padding-left: 17px;
 | 
			
		||||
    width: 28em;
 | 
			
		||||
    padding-bottom: 10px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.end-session-dialog-description:rtl {
 | 
			
		||||
    padding-right: 17px;
 | 
			
		||||
    width: 28em;
 | 
			
		||||
    padding-bottom: 10px;
 | 
			
		||||
    text-align: right;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.end-session-dialog-warning {
 | 
			
		||||
    width: 28em;
 | 
			
		||||
    color: #f57900;
 | 
			
		||||
    padding-top: 6px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.end-session-dialog-warning:rtl {
 | 
			
		||||
    width: 28em;
 | 
			
		||||
    color: #f57900;
 | 
			
		||||
    padding-top: 6px;
 | 
			
		||||
    text-align: right;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.end-session-dialog-logout-icon {
 | 
			
		||||
    border: 2px solid #8b8b8b;
 | 
			
		||||
    border-radius: 5px;
 | 
			
		||||
    width: 32px;
 | 
			
		||||
    height: 32px;
 | 
			
		||||
    width: 48px;
 | 
			
		||||
    height: 48px;
 | 
			
		||||
    background-size: contain;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.end-session-dialog-shutdown-icon {
 | 
			
		||||
    color: #bebebe;
 | 
			
		||||
    width: 32px;
 | 
			
		||||
    height: 32px;
 | 
			
		||||
    width: 48px;
 | 
			
		||||
    height: 48px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.end-session-dialog-inhibitor-layout {
 | 
			
		||||
    spacing: 16px;
 | 
			
		||||
    max-height: 200px;
 | 
			
		||||
    padding-right: 50px;
 | 
			
		||||
    padding-left: 50px;
 | 
			
		||||
    padding-right: 65px;
 | 
			
		||||
    padding-left: 65px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.end-session-dialog-session-list,
 | 
			
		||||
.end-session-dialog-app-list {
 | 
			
		||||
    spacing: 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.end-session-dialog-list-header {
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.end-session-dialog-list-header:rtl {
 | 
			
		||||
    text-align: right;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.end-session-dialog-app-list-item,
 | 
			
		||||
.end-session-dialog-session-list-item {
 | 
			
		||||
    spacing: 1em;
 | 
			
		||||
@@ -2093,6 +2161,10 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    color: #666666;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.prompt-dialog-description:rtl {
 | 
			
		||||
    text-align: right;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.prompt-dialog-password-box {
 | 
			
		||||
    spacing: 1em;
 | 
			
		||||
    padding-bottom: 1em;
 | 
			
		||||
@@ -2319,6 +2391,8 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
.login-dialog-user-list-item {
 | 
			
		||||
    border-radius: 5px;
 | 
			
		||||
    padding: .2em;
 | 
			
		||||
    color: #bfbfbf;
 | 
			
		||||
    text-shadow: black 0px 2px 2px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-user-list-item:ltr {
 | 
			
		||||
@@ -2329,24 +2403,6 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    padding-left: 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-user-list-item .login-dialog-user-list-item-name {
 | 
			
		||||
    font-size: 20px;
 | 
			
		||||
    padding-left: 18px;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-user-list:expanded .login-dialog-user-list-item {
 | 
			
		||||
    color: #bfbfbf;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-user-list-item,
 | 
			
		||||
.login-dialog-user-list-item:hover .login-dialog-user-list-item-name,
 | 
			
		||||
.login-dialog-user-list:expanded .login-dialog-user-list-item:focus .login-dialog-user-list-item-name,
 | 
			
		||||
.login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in {
 | 
			
		||||
    color: #bfbfbf;
 | 
			
		||||
    text-shadow: black 0px 2px 2px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-user-list-item:hover {
 | 
			
		||||
    background-color: rgba(255,255,255,0.1);
 | 
			
		||||
}
 | 
			
		||||
@@ -2373,13 +2429,6 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    background-color: #8b8b8b;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-user-list-item-icon {
 | 
			
		||||
    border: 2px solid #8b8b8b;
 | 
			
		||||
    border-radius: 3px;
 | 
			
		||||
    width: 64px;
 | 
			
		||||
    height: 64px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-not-listed-label {
 | 
			
		||||
    font-size: 10.5pt;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
@@ -2387,6 +2436,10 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    padding-top: 1em;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-user-selection-box {
 | 
			
		||||
    padding: 100px 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-user-selection-box .login-dialog-not-listed-label {
 | 
			
		||||
    padding-left: 2px;
 | 
			
		||||
}
 | 
			
		||||
@@ -2396,12 +2449,13 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
    color: #E8E8E8;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-username {
 | 
			
		||||
.login-dialog-username,
 | 
			
		||||
.user-widget-label {
 | 
			
		||||
    font-size: 16pt;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    text-align: left;
 | 
			
		||||
    padding-left: 15px;
 | 
			
		||||
    text-shadow: black 0px 4px 3px 0px;
 | 
			
		||||
    text-shadow: rgba(0, 0, 0, 0.5) 0px 2px 1px 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-prompt-layout {
 | 
			
		||||
@@ -2426,6 +2480,7 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.login-dialog-session-list-button:hover,
 | 
			
		||||
.login-dialog-session-list-button:focus,
 | 
			
		||||
.login-dialog-session-list-button:active {
 | 
			
		||||
    color: white;
 | 
			
		||||
}
 | 
			
		||||
@@ -2492,12 +2547,14 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.user-widget-label {
 | 
			
		||||
    font-size: 20px;
 | 
			
		||||
    font-weight: bold;
 | 
			
		||||
    text-align: left;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.user-widget-label:ltr {
 | 
			
		||||
    padding-left: 18px;
 | 
			
		||||
    color:white;
 | 
			
		||||
    text-shadow: black 0px 4px 3px 0px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
.user-widget-label:rtl {
 | 
			
		||||
    padding-right: 18px;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Screen shield */
 | 
			
		||||
@@ -2624,4 +2681,5 @@ StScrollBar StButton#vhandle:active {
 | 
			
		||||
 | 
			
		||||
.background-menu {
 | 
			
		||||
    -boxpointer-gap: 4px;
 | 
			
		||||
    -arrow-rise: 0px;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										90
									
								
								data/theme/menu-arrow-symbolic.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										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  | 
							
								
								
									
										
											BIN
										
									
								
								data/wanda.png
									
									
									
									
									
								
							
							
						
						
									
										
											BIN
										
									
								
								data/wanda.png
									
									
									
									
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 Before Width: | Height: | Size: 13 KiB  | 
@@ -112,7 +112,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-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la
 | 
			
		||||
GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la
 | 
			
		||||
 | 
			
		||||
# This includes the standard gtk-doc make rules, copied by gtkdocize.
 | 
			
		||||
include $(top_srcdir)/gtk-doc.make
 | 
			
		||||
 
 | 
			
		||||
@@ -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" />
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										133
									
								
								js/Makefile.am
									
									
									
									
									
								
							
							
						
						
									
										133
									
								
								js/Makefile.am
									
									
									
									
									
								
							@@ -1,125 +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/authPrompt.js	\
 | 
			
		||||
	gdm/batch.js		\
 | 
			
		||||
	gdm/fingerprint.js	\
 | 
			
		||||
	gdm/loginDialog.js	\
 | 
			
		||||
	gdm/oVirt.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/objectManager.js	\
 | 
			
		||||
	misc/params.js		\
 | 
			
		||||
	misc/smartcardManager.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/focusCaretTracker.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/brightness.js	\
 | 
			
		||||
	ui/status/keyboard.js	\
 | 
			
		||||
	ui/status/network.js	\
 | 
			
		||||
	ui/status/power.js	\
 | 
			
		||||
	ui/status/rfkill.js	\
 | 
			
		||||
	ui/status/volume.js	\
 | 
			
		||||
	ui/status/bluetooth.js	\
 | 
			
		||||
	ui/status/screencast.js	\
 | 
			
		||||
	ui/status/system.js	\
 | 
			
		||||
	ui/switcherPopup.js	\
 | 
			
		||||
	ui/tweener.js		\
 | 
			
		||||
	ui/unlockDialog.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/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,15 @@ 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 GnomeShellProxy = Gio.DBusProxy.makeProxyWrapper(GnomeShellIface);
 | 
			
		||||
 | 
			
		||||
@@ -204,11 +206,11 @@ const Application = new Lang.Class({
 | 
			
		||||
    _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) {
 | 
			
		||||
    _extensionFound: function(finder, extension) {
 | 
			
		||||
        let iter = this._model.append();
 | 
			
		||||
        this._model.set(iter, [0, 1], [extension.uuid, extension.metadata.name]);
 | 
			
		||||
        this._extensionIters[extension.uuid] = iter;
 | 
			
		||||
 
 | 
			
		||||
@@ -80,6 +80,7 @@ const AuthPrompt = new Lang.Class({
 | 
			
		||||
                               if (event.get_key_symbol() == Clutter.KEY_Escape) {
 | 
			
		||||
                                   this.cancel();
 | 
			
		||||
                               }
 | 
			
		||||
                               return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
                           }));
 | 
			
		||||
 | 
			
		||||
        this._userWell = new St.Bin({ x_fill: true,
 | 
			
		||||
@@ -93,7 +94,7 @@ const AuthPrompt = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.add(this._label,
 | 
			
		||||
                       { expand: true,
 | 
			
		||||
                         x_fill: true,
 | 
			
		||||
                         x_fill: false,
 | 
			
		||||
                         y_fill: true,
 | 
			
		||||
                         x_align: St.Align.START });
 | 
			
		||||
        this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry',
 | 
			
		||||
@@ -111,7 +112,7 @@ const AuthPrompt = new Lang.Class({
 | 
			
		||||
        this._message = new St.Label({ opacity: 0,
 | 
			
		||||
                                       styleClass: 'login-dialog-message' });
 | 
			
		||||
        this._message.clutter_text.line_wrap = true;
 | 
			
		||||
        this.actor.add(this._message, { x_fill: true, y_align: St.Align.START });
 | 
			
		||||
        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 });
 | 
			
		||||
@@ -263,10 +264,8 @@ const AuthPrompt = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onReset: function() {
 | 
			
		||||
        if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) {
 | 
			
		||||
            this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
 | 
			
		||||
            this.reset();
 | 
			
		||||
        }
 | 
			
		||||
        this.verificationStatus = AuthPromptStatus.NOT_VERIFYING;
 | 
			
		||||
        this.reset();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addActorToDefaultButtonWell: function(actor) {
 | 
			
		||||
@@ -451,8 +450,7 @@ const AuthPrompt = new Lang.Class({
 | 
			
		||||
            // respond to the request with the username
 | 
			
		||||
            beginRequestType = BeginRequestType.PROVIDE_USERNAME;
 | 
			
		||||
        } else if (this._userVerifier.serviceIsForeground(GdmUtil.OVIRT_SERVICE_NAME) ||
 | 
			
		||||
                   (this.smartcardDetected &&
 | 
			
		||||
                    this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -38,6 +36,7 @@ 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;
 | 
			
		||||
@@ -59,7 +58,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,
 | 
			
		||||
@@ -68,39 +67,18 @@ 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();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -189,7 +167,6 @@ const UserList = new Lang.Class({
 | 
			
		||||
        for (let userName in this._items) {
 | 
			
		||||
            let item = this._items[userName];
 | 
			
		||||
            item.actor.sync_hover();
 | 
			
		||||
            item.syncStyleClasses();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -478,18 +455,6 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        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);
 | 
			
		||||
@@ -505,7 +470,31 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        this._sessionMenuButton.actor.show();
 | 
			
		||||
        this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor);
 | 
			
		||||
 | 
			
		||||
   },
 | 
			
		||||
        this._disableUserList = undefined;
 | 
			
		||||
        this._userListLoaded = false;
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
            GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, this._loadUserList));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateDisableUserList: function() {
 | 
			
		||||
        let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY);
 | 
			
		||||
@@ -548,9 +537,12 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._logoBin.destroy_all_children();
 | 
			
		||||
        if (this._logoFileUri)
 | 
			
		||||
        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));
 | 
			
		||||
                                                                      -1, _LOGO_ICON_HEIGHT,
 | 
			
		||||
                                                                      scaleFactor));
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateLogo: function() {
 | 
			
		||||
@@ -649,6 +641,36 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        this._showPrompt();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loginScreenSessionActivated: function() {
 | 
			
		||||
        if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFICATION_SUCCEEDED)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        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) {
 | 
			
		||||
        Tweener.addTween(this.actor,
 | 
			
		||||
                         { opacity: 0,
 | 
			
		||||
@@ -666,7 +688,7 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                           onComplete: function() {
 | 
			
		||||
                               Mainloop.idle_add(Lang.bind(this, function() {
 | 
			
		||||
                                   this._greeter.call_start_session_when_ready_sync(serviceName, true, null);
 | 
			
		||||
                                   return false;
 | 
			
		||||
                                   return GLib.SOURCE_REMOVE;
 | 
			
		||||
                               }));
 | 
			
		||||
                           },
 | 
			
		||||
                           onCompleteScope: this });
 | 
			
		||||
@@ -721,6 +743,7 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                                                     function() {
 | 
			
		||||
                                                                         this._timedLoginAnimationTime -= _TIMED_LOGIN_IDLE_THRESHOLD;
 | 
			
		||||
                                                                         hold.release();
 | 
			
		||||
                                                                         return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                                                     });
 | 
			
		||||
        return hold;
 | 
			
		||||
    },
 | 
			
		||||
@@ -785,7 +808,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) {
 | 
			
		||||
@@ -798,7 +821,7 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                    this._resetTimedLogin();
 | 
			
		||||
                                }
 | 
			
		||||
 | 
			
		||||
                                return false;
 | 
			
		||||
                                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
                             }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -824,6 +847,7 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showUserList: function() {
 | 
			
		||||
        this._ensureUserListLoaded();
 | 
			
		||||
        this._authPrompt.hide();
 | 
			
		||||
        this._sessionMenuButton.close();
 | 
			
		||||
        this._setUserListExpanded(true);
 | 
			
		||||
@@ -867,14 +891,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);
 | 
			
		||||
@@ -884,6 +911,8 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
                                  Lang.bind(this, function(userManager, user) {
 | 
			
		||||
                                      this._userList.removeUser(user);
 | 
			
		||||
                                  }));
 | 
			
		||||
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open: function() {
 | 
			
		||||
@@ -907,6 +936,10 @@ const LoginDialog = new Lang.Class({
 | 
			
		||||
        Main.ctrlAltTabManager.removeGroup(this.dialogLayout);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    cancel: function() {
 | 
			
		||||
        this._authPrompt.cancel();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addCharacter: function(unichar) {
 | 
			
		||||
        this._authPrompt.addCharacter(unichar);
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -4,11 +4,13 @@ const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const OVirtCredentialsIface = <interface name='org.ovirt.vdsm.Credentials'>
 | 
			
		||||
<signal name="UserAuthenticated">
 | 
			
		||||
    <arg type="s" name="token"/>
 | 
			
		||||
</signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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({
 | 
			
		||||
 
 | 
			
		||||
@@ -250,6 +250,7 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
                                                       Lang.bind(this, function() {
 | 
			
		||||
                                                           this._messageQueueTimeoutId = 0;
 | 
			
		||||
                                                           this._queueMessageTimeout();
 | 
			
		||||
                                                           return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                                       }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -297,7 +298,7 @@ const ShellUserVerifier = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY))
 | 
			
		||||
            smartcardDetected = false;
 | 
			
		||||
        else if (this.reauthenticating)
 | 
			
		||||
        else if (this._reauthOnly)
 | 
			
		||||
            smartcardDetected = this._smartcardManager.hasInsertedLoginToken();
 | 
			
		||||
        else
 | 
			
		||||
            smartcardDetected = this._smartcardManager.hasInsertedTokens();
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										114
									
								
								js/js-resources.gresource.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										114
									
								
								js/js-resources.gresource.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,114 @@
 | 
			
		||||
<?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/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);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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
									
									
									
									
									
								
							
							
						
						
									
										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,58 +7,67 @@ const Mainloop = imports.mainloop;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const SystemdLoginManagerIface = <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>;
 | 
			
		||||
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 ConsoleKitManagerIface = '<node> \
 | 
			
		||||
<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> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const ConsoleKitSessionIface = <interface name='org.freedesktop.ConsoleKit.Session'>
 | 
			
		||||
<signal name='Lock' />
 | 
			
		||||
<signal name='Unlock' />
 | 
			
		||||
</interface>;
 | 
			
		||||
const ConsoleKitSessionIface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.ConsoleKit.Session"> \
 | 
			
		||||
<signal name="Lock" /> \
 | 
			
		||||
<signal name="Unlock" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const ConsoleKitSession = Gio.DBusProxy.makeProxyWrapper(ConsoleKitSessionIface);
 | 
			
		||||
const ConsoleKitManager = Gio.DBusProxy.makeProxyWrapper(ConsoleKitManagerIface);
 | 
			
		||||
 
 | 
			
		||||
@@ -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({
 | 
			
		||||
 
 | 
			
		||||
@@ -8,19 +8,21 @@ const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
// Specified in the D-Bus specification here:
 | 
			
		||||
// http://dbus.freedesktop.org/doc/dbus-specification.html#standard-interfaces-objectmanager
 | 
			
		||||
const ObjectManagerIface = <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>;
 | 
			
		||||
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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -7,12 +7,14 @@ const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const ObjectManager = imports.misc.objectManager;
 | 
			
		||||
 | 
			
		||||
const SmartcardTokenIface = <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>;
 | 
			
		||||
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;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -89,7 +89,7 @@ function spawnApp(argv) {
 | 
			
		||||
        let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null,
 | 
			
		||||
                                                      Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION);
 | 
			
		||||
 | 
			
		||||
        let context = global.create_app_launch_context();
 | 
			
		||||
        let context = global.create_app_launch_context(0, -1);
 | 
			
		||||
        app.launch([], context);
 | 
			
		||||
    } catch(err) {
 | 
			
		||||
        _handleSpawnError(argv[0], err);
 | 
			
		||||
@@ -153,7 +153,7 @@ 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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -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) {
 | 
			
		||||
@@ -310,7 +319,7 @@ const AppSwitcherPopup = new Lang.Class({
 | 
			
		||||
            this._createThumbnails();
 | 
			
		||||
        this._thumbnailTimeoutId = 0;
 | 
			
		||||
        this._thumbnailsFocused = false;
 | 
			
		||||
        return false;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _destroyThumbnails : function() {
 | 
			
		||||
@@ -375,6 +384,9 @@ const WindowSwitcherPopup = new Lang.Class({
 | 
			
		||||
        this._switcherList = new WindowList(windows, mode);
 | 
			
		||||
        this._items = this._switcherList.icons;
 | 
			
		||||
 | 
			
		||||
        if (this._items.length == 0)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -397,7 +409,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() {
 | 
			
		||||
@@ -424,7 +440,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;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -454,9 +469,10 @@ const AppSwitcher = new Lang.Class({
 | 
			
		||||
            appIcon.cachedWindows = allWindows.filter(function(w) {
 | 
			
		||||
                return windowTracker.get_window_app (w) == appIcon.app;
 | 
			
		||||
            });
 | 
			
		||||
            if (workspace == null || appIcon.cachedWindows.length > 0) {
 | 
			
		||||
            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;
 | 
			
		||||
@@ -472,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);
 | 
			
		||||
@@ -488,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++) {
 | 
			
		||||
@@ -508,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) {
 | 
			
		||||
@@ -542,7 +564,7 @@ const AppSwitcher = new Lang.Class({
 | 
			
		||||
                                                        Lang.bind(this, function () {
 | 
			
		||||
                                                                            this._enterItem(index);
 | 
			
		||||
                                                                            this._mouseTimeOutId = 0;
 | 
			
		||||
                                                                            return false;
 | 
			
		||||
                                                                            return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                                        }));
 | 
			
		||||
        } else
 | 
			
		||||
           this._itemEntered(index);
 | 
			
		||||
@@ -643,17 +665,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
    },
 | 
			
		||||
@@ -59,7 +62,7 @@ const Animation = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _update: function() {
 | 
			
		||||
        this._showFrame(this._frame + 1);
 | 
			
		||||
        return true;
 | 
			
		||||
        return GLib.SOURCE_CONTINUE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _animationsLoaded: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,7 +5,6 @@ const Gio = imports.gi.Gio;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const GObject = imports.gi.GObject;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const GMenu = imports.gi.GMenu;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
@@ -47,24 +46,40 @@ const INDICATORS_ANIMATION_MAX_TIME = 0.75;
 | 
			
		||||
const PAGE_SWITCH_TRESHOLD = 0.2;
 | 
			
		||||
const PAGE_SWITCH_TIME = 0.3;
 | 
			
		||||
 | 
			
		||||
// Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too
 | 
			
		||||
function _loadCategory(dir, view) {
 | 
			
		||||
    let iter = dir.iter();
 | 
			
		||||
    let appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
    let nextType;
 | 
			
		||||
    while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
 | 
			
		||||
        if (nextType == GMenu.TreeItemType.ENTRY) {
 | 
			
		||||
            let entry = iter.get_entry();
 | 
			
		||||
            let app = appSystem.lookup_app_by_tree_entry(entry);
 | 
			
		||||
            if (!entry.get_app_info().get_nodisplay())
 | 
			
		||||
                view.addApp(app);
 | 
			
		||||
        } else if (nextType == GMenu.TreeItemType.DIRECTORY) {
 | 
			
		||||
            let itemDir = iter.get_directory();
 | 
			
		||||
            if (!itemDir.get_is_nodisplay())
 | 
			
		||||
                _loadCategory(itemDir, view);
 | 
			
		||||
const VIEWS_SWITCH_TIME = 0.4;
 | 
			
		||||
const VIEWS_SWITCH_ANIMATION_DELAY = 0.1;
 | 
			
		||||
 | 
			
		||||
function _getCategories(info) {
 | 
			
		||||
    let categoriesStr = info.get_categories();
 | 
			
		||||
    if (!categoriesStr)
 | 
			
		||||
        return [];
 | 
			
		||||
    return categoriesStr.split(';');
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _listsIntersect(a, b) {
 | 
			
		||||
    for (let itemA of a)
 | 
			
		||||
        if (b.indexOf(itemA) >= 0)
 | 
			
		||||
            return true;
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _getFolderName(folder) {
 | 
			
		||||
    let name = folder.get_string('name');
 | 
			
		||||
 | 
			
		||||
    if (folder.get_boolean('translate')) {
 | 
			
		||||
        let keyfile = new GLib.KeyFile();
 | 
			
		||||
        let path = 'desktop-directories/' + name;
 | 
			
		||||
 | 
			
		||||
        try {
 | 
			
		||||
            keyfile.load_from_data_dirs(path, GLib.KeyFileFlags.NONE);
 | 
			
		||||
            name = keyfile.get_locale_string('Desktop Entry', 'Name', null);
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            return name;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
    return name;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const BaseAppView = new Lang.Class({
 | 
			
		||||
    Name: 'BaseAppView',
 | 
			
		||||
@@ -84,6 +99,9 @@ const BaseAppView = new Lang.Class({
 | 
			
		||||
        else
 | 
			
		||||
            this._grid = new IconGrid.IconGrid(gridParams);
 | 
			
		||||
 | 
			
		||||
        this._grid.connect('key-focus-in', Lang.bind(this, function(grid, actor) {
 | 
			
		||||
            this._keyFocusIn(actor);
 | 
			
		||||
        }));
 | 
			
		||||
        // Standard hack for ClutterBinLayout
 | 
			
		||||
        this._grid.actor.x_expand = true;
 | 
			
		||||
 | 
			
		||||
@@ -91,46 +109,43 @@ const BaseAppView = new Lang.Class({
 | 
			
		||||
        this._allItems = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _keyFocusIn: function(actor) {
 | 
			
		||||
        // Nothing by default
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeAll: function() {
 | 
			
		||||
        this._grid.removeAll();
 | 
			
		||||
        this._grid.destroyAll();
 | 
			
		||||
        this._items = {};
 | 
			
		||||
        this._allItems = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getItemId: function(item) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    _redisplay: function() {
 | 
			
		||||
        this.removeAll();
 | 
			
		||||
        this._loadApps();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createItemIcon: function(item) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    getAllItems: function() {
 | 
			
		||||
        return this._allItems;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addItem: function(icon) {
 | 
			
		||||
        let id = icon.id;
 | 
			
		||||
        if (this._items[id] !== undefined)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._allItems.push(icon);
 | 
			
		||||
        this._items[id] = icon;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _compareItems: function(a, b) {
 | 
			
		||||
        throw new Error('Not implemented');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addItem: function(item) {
 | 
			
		||||
        let id = this._getItemId(item);
 | 
			
		||||
        if (this._items[id] !== undefined)
 | 
			
		||||
            return null;
 | 
			
		||||
 | 
			
		||||
        let itemIcon = this._createItemIcon(item);
 | 
			
		||||
        this._allItems.push(item);
 | 
			
		||||
        this._items[id] = itemIcon;
 | 
			
		||||
 | 
			
		||||
        return itemIcon;
 | 
			
		||||
        return a.name.localeCompare(b.name);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    loadGrid: function() {
 | 
			
		||||
        this._allItems.sort(Lang.bind(this, this._compareItems));
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._allItems.length; i++) {
 | 
			
		||||
            let id = this._getItemId(this._allItems[i]);
 | 
			
		||||
            if (!id)
 | 
			
		||||
                continue;
 | 
			
		||||
            this._grid.addItem(this._items[id]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._allItems.sort(this._compareItems);
 | 
			
		||||
        this._allItems.forEach(Lang.bind(this, function(item) {
 | 
			
		||||
            this._grid.addItem(item);
 | 
			
		||||
        }));
 | 
			
		||||
        this.emit('view-loaded');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -243,7 +258,8 @@ const PageIndicators = new Lang.Class({
 | 
			
		||||
            Tweener.addTween(children[i],
 | 
			
		||||
                             { translation_x: 0,
 | 
			
		||||
                               time: INDICATORS_BASE_TIME + delay * i,
 | 
			
		||||
                               transition: 'easeInOutQuad'
 | 
			
		||||
                               transition: 'easeInOutQuad',
 | 
			
		||||
                               delay: VIEWS_SWITCH_ANIMATION_DELAY
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
@@ -281,7 +297,7 @@ const AllView = new Lang.Class({
 | 
			
		||||
        this._pageIndicators.actor.connect('scroll-event', Lang.bind(this, this._onScroll));
 | 
			
		||||
        this.actor.add_actor(this._pageIndicators.actor);
 | 
			
		||||
 | 
			
		||||
        this._folderIcons = [];
 | 
			
		||||
        this.folderIcons = [];
 | 
			
		||||
 | 
			
		||||
        this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() });
 | 
			
		||||
        let box = new St.BoxLayout({ vertical: true });
 | 
			
		||||
@@ -346,6 +362,76 @@ const AllView = new Lang.Class({
 | 
			
		||||
                    this._keyPressEventId = 0;
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._redisplayWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay));
 | 
			
		||||
 | 
			
		||||
        Shell.AppSystem.get_default().connect('installed-changed', Lang.bind(this, function() {
 | 
			
		||||
            Main.queueDeferredWork(this._redisplayWorkId);
 | 
			
		||||
        }));
 | 
			
		||||
        this._folderSettings = new Gio.Settings({ schema: 'org.gnome.desktop.app-folders' });
 | 
			
		||||
        this._folderSettings.connect('changed::folder-children', Lang.bind(this, function() {
 | 
			
		||||
            Main.queueDeferredWork(this._redisplayWorkId);
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeAll: function() {
 | 
			
		||||
        this.folderIcons = [];
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _itemNameChanged: function(item) {
 | 
			
		||||
        // If an item's name changed, we can pluck it out of where it's
 | 
			
		||||
        // supposed to be and reinsert it where it's sorted.
 | 
			
		||||
        let oldIdx = this._allItems.indexOf(item);
 | 
			
		||||
        this._allItems.splice(oldIdx, 1);
 | 
			
		||||
        let newIdx = Util.insertSorted(this._allItems, item, this._compareItems);
 | 
			
		||||
 | 
			
		||||
        this._grid.removeItem(item);
 | 
			
		||||
        this._grid.addItem(item, newIdx);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _refilterApps: function() {
 | 
			
		||||
        this._allItems.forEach(function(icon) {
 | 
			
		||||
            if (icon instanceof AppIcon)
 | 
			
		||||
                icon.actor.visible = true;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.folderIcons.forEach(Lang.bind(this, function(folder) {
 | 
			
		||||
            let folderApps = folder.getAppIds();
 | 
			
		||||
            folderApps.forEach(Lang.bind(this, function(appId) {
 | 
			
		||||
                let appIcon = this._items[appId];
 | 
			
		||||
                appIcon.actor.visible = false;
 | 
			
		||||
            }));
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _loadApps: function() {
 | 
			
		||||
        let apps = Gio.AppInfo.get_all().filter(function(appInfo) {
 | 
			
		||||
            return appInfo.should_show();
 | 
			
		||||
        }).map(function(app) {
 | 
			
		||||
            return app.get_id();
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        let appSys = Shell.AppSystem.get_default();
 | 
			
		||||
 | 
			
		||||
        let folders = this._folderSettings.get_strv('folder-children');
 | 
			
		||||
        folders.forEach(Lang.bind(this, function(id) {
 | 
			
		||||
            let path = this._folderSettings.path + 'folders/' + id + '/';
 | 
			
		||||
            let icon = new FolderIcon(id, path, this);
 | 
			
		||||
            icon.connect('name-changed', Lang.bind(this, this._itemNameChanged));
 | 
			
		||||
            icon.connect('apps-changed', Lang.bind(this, this._refilterApps));
 | 
			
		||||
            this.addItem(icon);
 | 
			
		||||
            this.folderIcons.push(icon);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        apps.forEach(Lang.bind(this, function(appId) {
 | 
			
		||||
            let app = appSys.lookup_app(appId);
 | 
			
		||||
            let icon = new AppIcon(app);
 | 
			
		||||
            this.addItem(icon);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this.loadGrid();
 | 
			
		||||
        this._refilterApps();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getCurrentPageY: function() {
 | 
			
		||||
@@ -415,7 +501,7 @@ const AllView = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onScroll: function(actor, event) {
 | 
			
		||||
        if (this._displayingPopup)
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
 | 
			
		||||
        let direction = event.get_scroll_direction();
 | 
			
		||||
        if (direction == Clutter.ScrollDirection.UP)
 | 
			
		||||
@@ -423,7 +509,7 @@ const AllView = new Lang.Class({
 | 
			
		||||
        else if (direction == Clutter.ScrollDirection.DOWN)
 | 
			
		||||
            this.goToPage(this._currentPage + 1);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPan: function(action) {
 | 
			
		||||
@@ -454,63 +540,17 @@ const AllView = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onKeyPressEvent: function(actor, event) {
 | 
			
		||||
        if (this._displayingPopup)
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
 | 
			
		||||
        if (event.get_key_symbol() == Clutter.Page_Up) {
 | 
			
		||||
            this.goToPage(this._currentPage - 1);
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        } else if (event.get_key_symbol() == Clutter.Page_Down) {
 | 
			
		||||
            this.goToPage(this._currentPage + 1);
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getItemId: function(item) {
 | 
			
		||||
        if (item instanceof Shell.App)
 | 
			
		||||
            return item.get_id();
 | 
			
		||||
        else if (item instanceof GMenu.TreeDirectory)
 | 
			
		||||
            return item.get_menu_id();
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createItemIcon: function(item) {
 | 
			
		||||
        if (item instanceof Shell.App)
 | 
			
		||||
            return new AppIcon(item);
 | 
			
		||||
        else if (item instanceof GMenu.TreeDirectory)
 | 
			
		||||
            return new FolderIcon(item, this);
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _compareItems: function(itemA, itemB) {
 | 
			
		||||
        // bit of a hack: rely on both ShellApp and GMenuTreeDirectory
 | 
			
		||||
        // having a get_name() method
 | 
			
		||||
        let nameA = GLib.utf8_collate_key(itemA.get_name(), -1);
 | 
			
		||||
        let nameB = GLib.utf8_collate_key(itemB.get_name(), -1);
 | 
			
		||||
        return (nameA > nameB) ? 1 : (nameA < nameB ? -1 : 0);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeAll: function() {
 | 
			
		||||
        this._folderIcons = [];
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addApp: function(app) {
 | 
			
		||||
        let appIcon = this._addItem(app);
 | 
			
		||||
        if (appIcon)
 | 
			
		||||
            appIcon.actor.connect('key-focus-in',
 | 
			
		||||
                                  Lang.bind(this, this._ensureIconVisible));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addFolder: function(dir) {
 | 
			
		||||
        let folderIcon = this._addItem(dir);
 | 
			
		||||
        this._folderIcons.push(folderIcon);
 | 
			
		||||
        if (folderIcon)
 | 
			
		||||
            folderIcon.actor.connect('key-focus-in',
 | 
			
		||||
                                     Lang.bind(this, this._ensureIconVisible));
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addFolderPopup: function(popup) {
 | 
			
		||||
@@ -525,7 +565,7 @@ const AllView = new Lang.Class({
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureIconVisible: function(icon) {
 | 
			
		||||
    _keyFocusIn: function(icon) {
 | 
			
		||||
        let itemPage = this._grid.getItemPage(icon);
 | 
			
		||||
        this.goToPage(itemPage);
 | 
			
		||||
    },
 | 
			
		||||
@@ -577,8 +617,8 @@ const AllView = new Lang.Class({
 | 
			
		||||
        this._availWidth = availWidth;
 | 
			
		||||
        this._availHeight = availHeight;
 | 
			
		||||
        // Update folder views
 | 
			
		||||
        for (let i = 0; i < this._folderIcons.length; i++)
 | 
			
		||||
            this._folderIcons[i].adaptToSize(availWidth, availHeight);
 | 
			
		||||
        for (let i = 0; i < this.folderIcons.length; i++)
 | 
			
		||||
            this.folderIcons[i].adaptToSize(availWidth, availHeight);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(AllView.prototype);
 | 
			
		||||
@@ -607,13 +647,18 @@ const FrequentView = new Lang.Class({
 | 
			
		||||
        this._noFrequentAppsLabel.hide();
 | 
			
		||||
 | 
			
		||||
        this._usage = Shell.AppUsage.get_default();
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('notify::mapped', Lang.bind(this, function() {
 | 
			
		||||
            if (this.actor.mapped)
 | 
			
		||||
                this._redisplay();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    hasUsefulData: function() {
 | 
			
		||||
        return this._usage.get_most_used("").length >= MIN_FREQUENT_APPS_COUNT;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    loadApps: function() {
 | 
			
		||||
    _loadApps: function() {
 | 
			
		||||
        let mostUsed = this._usage.get_most_used ("");
 | 
			
		||||
        let hasUsefulData = this.hasUsefulData();
 | 
			
		||||
        this._noFrequentAppsLabel.visible = !hasUsefulData;
 | 
			
		||||
@@ -691,16 +736,6 @@ const AppDisplay = new Lang.Class({
 | 
			
		||||
    Name: 'AppDisplay',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
        this._appSystem.connect('installed-changed', Lang.bind(this, function() {
 | 
			
		||||
            Main.queueDeferredWork(this._allAppsWorkId);
 | 
			
		||||
        }));
 | 
			
		||||
        Main.overview.connect('showing', Lang.bind(this, function() {
 | 
			
		||||
            Main.queueDeferredWork(this._frequentAppsWorkId);
 | 
			
		||||
        }));
 | 
			
		||||
        global.settings.connect('changed::app-folder-categories', Lang.bind(this, function() {
 | 
			
		||||
            Main.queueDeferredWork(this._allAppsWorkId);
 | 
			
		||||
        }));
 | 
			
		||||
        this._privacySettings = new Gio.Settings({ schema: 'org.gnome.desktop.privacy' });
 | 
			
		||||
        this._privacySettings.connect('changed::remember-app-usage',
 | 
			
		||||
                                      Lang.bind(this, this._updateFrequentVisibility));
 | 
			
		||||
@@ -754,22 +789,15 @@ const AppDisplay = new Lang.Class({
 | 
			
		||||
            initialView = Views.ALL;
 | 
			
		||||
        this._showView(initialView);
 | 
			
		||||
        this._updateFrequentVisibility();
 | 
			
		||||
 | 
			
		||||
        // We need a dummy actor to catch the keyboard focus if the
 | 
			
		||||
        // user Ctrl-Alt-Tabs here before the deferred work creates
 | 
			
		||||
        // our real contents
 | 
			
		||||
        this._focusDummy = new St.Bin({ can_focus: true });
 | 
			
		||||
        this._viewStack.add_actor(this._focusDummy);
 | 
			
		||||
 | 
			
		||||
        this._allAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayAllApps));
 | 
			
		||||
        this._frequentAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayFrequentApps));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _showView: function(activeIndex) {
 | 
			
		||||
        for (let i = 0; i < this._views.length; i++) {
 | 
			
		||||
            let actor = this._views[i].view.actor;
 | 
			
		||||
            let params = { time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME,
 | 
			
		||||
                           opacity: (i == activeIndex) ? 255 : 0 };
 | 
			
		||||
 | 
			
		||||
            let params = { time: VIEWS_SWITCH_TIME,
 | 
			
		||||
                           opacity: (i == activeIndex) ? 255 : 0,
 | 
			
		||||
                           delay: (i == activeIndex) ? VIEWS_SWITCH_ANIMATION_DELAY : 0 };
 | 
			
		||||
            if (i == activeIndex)
 | 
			
		||||
                actor.visible = true;
 | 
			
		||||
            else
 | 
			
		||||
@@ -796,52 +824,6 @@ const AppDisplay = new Lang.Class({
 | 
			
		||||
            this._showView(Views.ALL);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _redisplay: function() {
 | 
			
		||||
        this._redisplayFrequentApps();
 | 
			
		||||
        this._redisplayAllApps();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _redisplayFrequentApps: function() {
 | 
			
		||||
        let view = this._views[Views.FREQUENT].view;
 | 
			
		||||
 | 
			
		||||
        view.removeAll();
 | 
			
		||||
        view.loadApps();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _redisplayAllApps: function() {
 | 
			
		||||
        let view = this._views[Views.ALL].view;
 | 
			
		||||
 | 
			
		||||
        view.removeAll();
 | 
			
		||||
 | 
			
		||||
        let tree = this._appSystem.get_tree();
 | 
			
		||||
        let root = tree.get_root_directory();
 | 
			
		||||
 | 
			
		||||
        let iter = root.iter();
 | 
			
		||||
        let nextType;
 | 
			
		||||
        let folderCategories = global.settings.get_strv('app-folder-categories');
 | 
			
		||||
        while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) {
 | 
			
		||||
            if (nextType == GMenu.TreeItemType.DIRECTORY) {
 | 
			
		||||
                let dir = iter.get_directory();
 | 
			
		||||
                if (dir.get_is_nodisplay())
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                if (folderCategories.indexOf(dir.get_menu_id()) != -1)
 | 
			
		||||
                    view.addFolder(dir);
 | 
			
		||||
                else
 | 
			
		||||
                    _loadCategory(dir, view);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        view.loadGrid();
 | 
			
		||||
 | 
			
		||||
        if (this._focusDummy) {
 | 
			
		||||
            let focused = this._focusDummy.has_key_focus();
 | 
			
		||||
            this._focusDummy.destroy();
 | 
			
		||||
            this._focusDummy = null;
 | 
			
		||||
            if (focused)
 | 
			
		||||
                this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    selectApp: function(id) {
 | 
			
		||||
        this._showView(Views.ALL);
 | 
			
		||||
        this._views[Views.ALL].view.selectApp(id);
 | 
			
		||||
@@ -871,8 +853,8 @@ const AppSearchProvider = new Lang.Class({
 | 
			
		||||
    getResultMetas: function(apps, callback) {
 | 
			
		||||
        let metas = [];
 | 
			
		||||
        for (let i = 0; i < apps.length; i++) {
 | 
			
		||||
            let app = apps[i];
 | 
			
		||||
            metas.push({ 'id': app,
 | 
			
		||||
            let app = this._appSys.lookup_app(apps[i]);
 | 
			
		||||
            metas.push({ 'id': app.get_id(),
 | 
			
		||||
                         'name': app.get_name(),
 | 
			
		||||
                         'createIcon': function(size) {
 | 
			
		||||
                             return app.create_icon_texture(size);
 | 
			
		||||
@@ -886,15 +868,29 @@ const AppSearchProvider = new Lang.Class({
 | 
			
		||||
        return results.slice(0, maxNumber);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getInitialResultSet: function(terms) {
 | 
			
		||||
        this.searchSystem.setResults(this, this._appSys.initial_search(terms));
 | 
			
		||||
    getInitialResultSet: function(terms, callback, cancellable) {
 | 
			
		||||
        let query = terms.join(' ');
 | 
			
		||||
        let groups = Gio.DesktopAppInfo.search(query);
 | 
			
		||||
        let usage = Shell.AppUsage.get_default();
 | 
			
		||||
        let results = [];
 | 
			
		||||
        groups.forEach(function(group) {
 | 
			
		||||
            group = group.filter(function(appID) {
 | 
			
		||||
                let app = Gio.DesktopAppInfo.new(appID);
 | 
			
		||||
                return app && app.should_show();
 | 
			
		||||
            });
 | 
			
		||||
            results = results.concat(group.sort(function(a, b) {
 | 
			
		||||
                return usage.compare('', a, b);
 | 
			
		||||
            }));
 | 
			
		||||
        });
 | 
			
		||||
        callback(results);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSubsearchResultSet: function(previousResults, terms) {
 | 
			
		||||
        this.searchSystem.setResults(this, this._appSys.subsearch(previousResults, terms));
 | 
			
		||||
    getSubsearchResultSet: function(previousResults, terms, callback, cancellable) {
 | 
			
		||||
        this.getInitialResultSet(terms, callback, cancellable);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateResult: function(app) {
 | 
			
		||||
    activateResult: function(result) {
 | 
			
		||||
        let app = this._appSys.lookup_app(result);
 | 
			
		||||
        let event = Clutter.get_current_event();
 | 
			
		||||
        let modifiers = event ? event.get_state() : 0;
 | 
			
		||||
        let openNewWindow = modifiers & Clutter.ModifierType.CONTROL_MASK;
 | 
			
		||||
@@ -913,8 +909,8 @@ const AppSearchProvider = new Lang.Class({
 | 
			
		||||
        app.open_new_window(workspace);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createResultObject: function (resultMeta, terms) {
 | 
			
		||||
        let app = resultMeta['id'];
 | 
			
		||||
    createResultObject: function (resultMeta) {
 | 
			
		||||
        let app = this._appSys.lookup_app(resultMeta['id']);
 | 
			
		||||
        return new AppIcon(app);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -940,36 +936,27 @@ const FolderView = new Lang.Class({
 | 
			
		||||
        this.actor.add_action(action);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getItemId: function(item) {
 | 
			
		||||
        return item.get_id();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createItemIcon: function(item) {
 | 
			
		||||
        return new AppIcon(item);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _compareItems: function(a, b) {
 | 
			
		||||
        return a.compare_by_name(b);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addApp: function(app) {
 | 
			
		||||
        this._addItem(app);
 | 
			
		||||
    _keyFocusIn: function(actor) {
 | 
			
		||||
        Util.ensureActorVisibleInScrollView(this.actor, actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createFolderIcon: function(size) {
 | 
			
		||||
        let icon = new St.Widget({ layout_manager: new Clutter.BinLayout(),
 | 
			
		||||
                                   style_class: 'app-folder-icon',
 | 
			
		||||
                                   width: size, height: size });
 | 
			
		||||
        let layout = new Clutter.TableLayout();
 | 
			
		||||
        let icon = new St.Widget({ layout_manager: layout,
 | 
			
		||||
                                   style_class: 'app-folder-icon' });
 | 
			
		||||
        layout.hookup_style(icon);
 | 
			
		||||
        let subSize = Math.floor(FOLDER_SUBICON_FRACTION * size);
 | 
			
		||||
 | 
			
		||||
        let aligns = [ Clutter.ActorAlign.START, Clutter.ActorAlign.END ];
 | 
			
		||||
        for (let i = 0; i < Math.min(this._allItems.length, 4); i++) {
 | 
			
		||||
            let texture = this._allItems[i].create_icon_texture(subSize);
 | 
			
		||||
            let bin = new St.Bin({ child: texture,
 | 
			
		||||
                                   x_expand: true, y_expand: true });
 | 
			
		||||
            bin.set_x_align(aligns[i % 2]);
 | 
			
		||||
            bin.set_y_align(aligns[Math.floor(i / 2)]);
 | 
			
		||||
            icon.add_actor(bin);
 | 
			
		||||
        let numItems = this._allItems.length;
 | 
			
		||||
        for (let i = 0; i < 4; i++) {
 | 
			
		||||
            let bin;
 | 
			
		||||
            if (i < numItems) {
 | 
			
		||||
                let texture = this._allItems[i].app.create_icon_texture(subSize);
 | 
			
		||||
                bin = new St.Bin({ child: texture });
 | 
			
		||||
            } else {
 | 
			
		||||
                bin = new St.Bin({ width: subSize, height: subSize });
 | 
			
		||||
            }
 | 
			
		||||
            layout.pack(bin, i % 2, Math.floor(i / 2));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return icon;
 | 
			
		||||
@@ -1041,10 +1028,12 @@ const FolderView = new Lang.Class({
 | 
			
		||||
const FolderIcon = new Lang.Class({
 | 
			
		||||
    Name: 'FolderIcon',
 | 
			
		||||
 | 
			
		||||
    _init: function(dir, parentView) {
 | 
			
		||||
        this._dir = dir;
 | 
			
		||||
    _init: function(id, path, parentView) {
 | 
			
		||||
        this.id = id;
 | 
			
		||||
        this._parentView = parentView;
 | 
			
		||||
 | 
			
		||||
        this._folder = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders.folder',
 | 
			
		||||
                                          path: path });
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'app-well-app app-folder',
 | 
			
		||||
                                     button_mask: St.ButtonMask.ONE,
 | 
			
		||||
                                     toggle_mode: true,
 | 
			
		||||
@@ -1055,15 +1044,11 @@ const FolderIcon = new Lang.Class({
 | 
			
		||||
        // whether we need to update arrow side, position etc.
 | 
			
		||||
        this._popupInvalidated = false;
 | 
			
		||||
 | 
			
		||||
        let label = this._dir.get_name();
 | 
			
		||||
        this.icon = new IconGrid.BaseIcon(label,
 | 
			
		||||
                                          { createIcon: Lang.bind(this, this._createIcon), setSizeManually: true });
 | 
			
		||||
        this.icon = new IconGrid.BaseIcon('', { createIcon: Lang.bind(this, this._createIcon), setSizeManually: true });
 | 
			
		||||
        this.actor.set_child(this.icon.actor);
 | 
			
		||||
        this.actor.label_actor = this.icon.label;
 | 
			
		||||
 | 
			
		||||
        this.view = new FolderView();
 | 
			
		||||
        _loadCategory(dir, this.view);
 | 
			
		||||
        this.view.loadGrid();
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
@@ -1076,6 +1061,63 @@ const FolderIcon = new Lang.Class({
 | 
			
		||||
                if (!this.actor.mapped && this._popup)
 | 
			
		||||
                    this._popup.popdown();
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._folder.connect('changed', Lang.bind(this, this._redisplay));
 | 
			
		||||
        this._redisplay();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getAppIds: function() {
 | 
			
		||||
        return this.view.getAllItems().map(function(item) {
 | 
			
		||||
            return item.id;
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateName: function() {
 | 
			
		||||
        let name = _getFolderName(this._folder);
 | 
			
		||||
        if (this.name == name)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this.name = name;
 | 
			
		||||
        this.icon.label.text = this.name;
 | 
			
		||||
        this.emit('name-changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _redisplay: function() {
 | 
			
		||||
        this._updateName();
 | 
			
		||||
 | 
			
		||||
        this.view.removeAll();
 | 
			
		||||
 | 
			
		||||
        let excludedApps = this._folder.get_strv('excluded-apps');
 | 
			
		||||
        let appSys = Shell.AppSystem.get_default();
 | 
			
		||||
        let addAppId = (function addAppId(appId) {
 | 
			
		||||
            if (excludedApps.indexOf(appId) >= 0)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            let app = appSys.lookup_app(appId);
 | 
			
		||||
            if (!app)
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            if (!app.get_app_info().should_show())
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            let icon = new AppIcon(app);
 | 
			
		||||
            this.view.addItem(icon);
 | 
			
		||||
        }).bind(this);
 | 
			
		||||
 | 
			
		||||
        let folderApps = this._folder.get_strv('apps');
 | 
			
		||||
        folderApps.forEach(addAppId);
 | 
			
		||||
 | 
			
		||||
        let folderCategories = this._folder.get_strv('categories');
 | 
			
		||||
        Gio.AppInfo.get_all().forEach(function(appInfo) {
 | 
			
		||||
            let appCategories = _getCategories(appInfo);
 | 
			
		||||
            if (!_listsIntersect(folderCategories, appCategories))
 | 
			
		||||
                return;
 | 
			
		||||
 | 
			
		||||
            addAppId(appInfo.get_id());
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        this.view.loadGrid();
 | 
			
		||||
        this.emit('apps-changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createIcon: function(iconSize) {
 | 
			
		||||
@@ -1154,6 +1196,7 @@ const FolderIcon = new Lang.Class({
 | 
			
		||||
        this._popupInvalidated = true;
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(FolderIcon.prototype);
 | 
			
		||||
 | 
			
		||||
const AppFolderPopup = new Lang.Class({
 | 
			
		||||
    Name: 'AppFolderPopup',
 | 
			
		||||
@@ -1207,13 +1250,13 @@ const AppFolderPopup = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onKeyPress: function(actor, event) {
 | 
			
		||||
        if (!this._isOpen)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        if (event.get_key_symbol() != Clutter.KEY_Escape)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        this.popdown();
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    toggle: function() {
 | 
			
		||||
@@ -1272,6 +1315,9 @@ const AppIcon = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init : function(app, iconParams) {
 | 
			
		||||
        this.app = app;
 | 
			
		||||
        this.id = app.get_id();
 | 
			
		||||
        this.name = app.get_name();
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'app-well-app',
 | 
			
		||||
                                     reactive: true,
 | 
			
		||||
                                     button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO,
 | 
			
		||||
@@ -1352,13 +1398,15 @@ const AppIcon = new Lang.Class({
 | 
			
		||||
            this._removeMenuTimeout();
 | 
			
		||||
            this._menuTimeoutId = Mainloop.timeout_add(MENU_POPUP_TIMEOUT,
 | 
			
		||||
                Lang.bind(this, function() {
 | 
			
		||||
                    this._menuTimeoutId = 0;
 | 
			
		||||
                    this.popupMenu();
 | 
			
		||||
                    return GLib.SOURCE_REMOVE;
 | 
			
		||||
                }));
 | 
			
		||||
        } else if (button == 3) {
 | 
			
		||||
            this.popupMenu();
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onClicked: function(actor, button) {
 | 
			
		||||
@@ -1367,10 +1415,6 @@ const AppIcon = new Lang.Class({
 | 
			
		||||
        if (button == 1) {
 | 
			
		||||
            this._onActivate(Clutter.get_current_event());
 | 
			
		||||
        } else if (button == 2) {
 | 
			
		||||
            // Last workspace is always empty
 | 
			
		||||
            let launchWorkspace = global.screen.get_workspace_by_index(global.screen.n_workspaces - 1);
 | 
			
		||||
            launchWorkspace.activate(global.get_current_time());
 | 
			
		||||
            this.emit('launching');
 | 
			
		||||
            this.app.open_new_window(-1);
 | 
			
		||||
            Main.overview.hide();
 | 
			
		||||
        }
 | 
			
		||||
@@ -1429,7 +1473,6 @@ const AppIcon = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivate: function (event) {
 | 
			
		||||
        this.emit('launching');
 | 
			
		||||
        let modifiers = event.get_state();
 | 
			
		||||
 | 
			
		||||
        if (modifiers & Clutter.ModifierType.CONTROL_MASK
 | 
			
		||||
@@ -1481,8 +1524,6 @@ const AppIconMenu = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._source = source;
 | 
			
		||||
 | 
			
		||||
        this.connect('activate', Lang.bind(this, this._onActivate));
 | 
			
		||||
 | 
			
		||||
        this.actor.add_style_class_name('app-well-menu');
 | 
			
		||||
 | 
			
		||||
        // Chain our visibility and lifecycle to that of the source
 | 
			
		||||
@@ -1498,7 +1539,9 @@ const AppIconMenu = new Lang.Class({
 | 
			
		||||
    _redisplay: function() {
 | 
			
		||||
        this.removeAll();
 | 
			
		||||
 | 
			
		||||
        let windows = this._source.app.get_windows();
 | 
			
		||||
        let windows = this._source.app.get_windows().filter(function(w) {
 | 
			
		||||
            return !w.skip_taskbar;
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        // Display the app windows menu items and the separator between windows
 | 
			
		||||
        // of the current desktop and other windows.
 | 
			
		||||
@@ -1506,25 +1549,54 @@ const AppIconMenu = new Lang.Class({
 | 
			
		||||
        let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < windows.length; i++) {
 | 
			
		||||
            if (!separatorShown && windows[i].get_workspace() != activeWorkspace) {
 | 
			
		||||
            let window = windows[i];
 | 
			
		||||
            if (!separatorShown && window.get_workspace() != activeWorkspace) {
 | 
			
		||||
                this._appendSeparator();
 | 
			
		||||
                separatorShown = true;
 | 
			
		||||
            }
 | 
			
		||||
            let item = this._appendMenuItem(windows[i].title);
 | 
			
		||||
            item._window = windows[i];
 | 
			
		||||
            let item = this._appendMenuItem(window.title);
 | 
			
		||||
            item.connect('activate', Lang.bind(this, function() {
 | 
			
		||||
                this.emit('activate-window', window);
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!this._source.app.is_window_backed()) {
 | 
			
		||||
            if (windows.length > 0)
 | 
			
		||||
                this._appendSeparator();
 | 
			
		||||
            this._appendSeparator();
 | 
			
		||||
 | 
			
		||||
            this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
 | 
			
		||||
            this._newWindowMenuItem.connect('activate', Lang.bind(this, function() {
 | 
			
		||||
                this._source.app.open_new_window(-1);
 | 
			
		||||
                this.emit('activate-window', null);
 | 
			
		||||
            }));
 | 
			
		||||
            this._appendSeparator();
 | 
			
		||||
 | 
			
		||||
            let appInfo = this._source.app.get_app_info();
 | 
			
		||||
            let actions = appInfo.list_actions();
 | 
			
		||||
            for (let i = 0; i < actions.length; i++) {
 | 
			
		||||
                let action = actions[i];
 | 
			
		||||
                let item = this._appendMenuItem(appInfo.get_action_name(action));
 | 
			
		||||
                item.connect('activate', Lang.bind(this, function(emitter, event) {
 | 
			
		||||
                    this._source.app.launch_action(action, event.get_time(), -1);
 | 
			
		||||
                    this.emit('activate-window', null);
 | 
			
		||||
                }));
 | 
			
		||||
            }
 | 
			
		||||
            this._appendSeparator();
 | 
			
		||||
 | 
			
		||||
            let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id());
 | 
			
		||||
 | 
			
		||||
            this._newWindowMenuItem = this._appendMenuItem(_("New Window"));
 | 
			
		||||
            this._appendSeparator();
 | 
			
		||||
 | 
			
		||||
            this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites")
 | 
			
		||||
                                                                : _("Add to Favorites"));
 | 
			
		||||
            if (isFavorite) {
 | 
			
		||||
                let item = this._appendMenuItem(_("Remove from Favorites"));
 | 
			
		||||
                item.connect('activate', Lang.bind(this, function() {
 | 
			
		||||
                    let favs = AppFavorites.getAppFavorites();
 | 
			
		||||
                    favs.removeFavorite(this._source.app.get_id());
 | 
			
		||||
                }));
 | 
			
		||||
            } else {
 | 
			
		||||
                let item = this._appendMenuItem(_("Add to Favorites"));
 | 
			
		||||
                item.connect('activate', Lang.bind(this, function() {
 | 
			
		||||
                    let favs = AppFavorites.getAppFavorites();
 | 
			
		||||
                    favs.addFavorite(this._source.app.get_id());
 | 
			
		||||
                }));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -1543,24 +1615,6 @@ const AppIconMenu = new Lang.Class({
 | 
			
		||||
    popup: function(activatingButton) {
 | 
			
		||||
        this._redisplay();
 | 
			
		||||
        this.open();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActivate: function (actor, child) {
 | 
			
		||||
        if (child._window) {
 | 
			
		||||
            let metaWindow = child._window;
 | 
			
		||||
            this.emit('activate-window', metaWindow);
 | 
			
		||||
        } else if (child == this._newWindowMenuItem) {
 | 
			
		||||
            this._source.app.open_new_window(-1);
 | 
			
		||||
            this.emit('activate-window', null);
 | 
			
		||||
        } else if (child == this._toggleFavoriteMenuItem) {
 | 
			
		||||
            let favs = AppFavorites.getAppFavorites();
 | 
			
		||||
            let isFavorite = favs.isFavorite(this._source.app.get_id());
 | 
			
		||||
            if (isFavorite)
 | 
			
		||||
                favs.removeFavorite(this._source.app.get_id());
 | 
			
		||||
            else
 | 
			
		||||
                favs.addFavorite(this._source.app.get_id());
 | 
			
		||||
        }
 | 
			
		||||
        this.close();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(AppIconMenu.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -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];
 | 
			
		||||
@@ -262,6 +273,7 @@ const BackgroundCache = new Lang.Class({
 | 
			
		||||
            if (params.onLoaded) {
 | 
			
		||||
                GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
 | 
			
		||||
                    params.onLoaded(this._animation);
 | 
			
		||||
                    return GLib.SOURCE_REMOVE;
 | 
			
		||||
                }));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -276,6 +288,7 @@ const BackgroundCache = new Lang.Class({
 | 
			
		||||
                           if (params.onLoaded) {
 | 
			
		||||
                               GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
 | 
			
		||||
                                   params.onLoaded(this._animation);
 | 
			
		||||
                                   return GLib.SOURCE_REMOVE;
 | 
			
		||||
                               }));
 | 
			
		||||
                           }
 | 
			
		||||
                       }));
 | 
			
		||||
@@ -315,7 +328,6 @@ 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;
 | 
			
		||||
 | 
			
		||||
@@ -376,7 +388,7 @@ const Background = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() {
 | 
			
		||||
            this.emit('loaded');
 | 
			
		||||
            return false;
 | 
			
		||||
            return GLib.SOURCE_REMOVE;
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -415,29 +427,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);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -485,11 +494,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();
 | 
			
		||||
@@ -524,7 +530,7 @@ const Background = new Lang.Class({
 | 
			
		||||
                                                      Lang.bind(this, function() {
 | 
			
		||||
                                                                    this._updateAnimationTimeoutId = 0;
 | 
			
		||||
                                                                    this._updateAnimation();
 | 
			
		||||
                                                                    return false;
 | 
			
		||||
                                                                    return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                                                }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -544,30 +550,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) {
 | 
			
		||||
@@ -590,24 +599,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;
 | 
			
		||||
    },
 | 
			
		||||
@@ -659,7 +650,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);
 | 
			
		||||
 | 
			
		||||
@@ -747,30 +744,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');
 | 
			
		||||
                                   })
 | 
			
		||||
@@ -798,7 +796,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;
 | 
			
		||||
    });
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -69,7 +69,7 @@ const BoxPointer = new Lang.Class({
 | 
			
		||||
    _muteInput: function() {
 | 
			
		||||
        if (this._capturedEventId == 0)
 | 
			
		||||
            this._capturedEventId = this.actor.connect('captured-event',
 | 
			
		||||
                                                       function() { return true; });
 | 
			
		||||
                                                       function() { return Clutter.EVENT_STOP; });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _unmuteInput: function() {
 | 
			
		||||
@@ -121,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();
 | 
			
		||||
@@ -284,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);
 | 
			
		||||
@@ -337,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);
 | 
			
		||||
@@ -358,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);
 | 
			
		||||
@@ -379,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -17,16 +17,18 @@ const SHOW_WEEKDATE_KEY = 'show-weekdate';
 | 
			
		||||
// in org.gnome.desktop.interface
 | 
			
		||||
const CLOCK_FORMAT_KEY        = 'clock-format';
 | 
			
		||||
 | 
			
		||||
function _sameDay(dateA, dateB) {
 | 
			
		||||
    return (dateA.getDate() == dateB.getDate() &&
 | 
			
		||||
            dateA.getMonth() == dateB.getMonth() &&
 | 
			
		||||
            dateA.getYear() == dateB.getYear());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _sameYear(dateA, dateB) {
 | 
			
		||||
    return (dateA.getYear() == dateB.getYear());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 +192,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 +331,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);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -402,6 +403,8 @@ const Calendar = new Lang.Class({
 | 
			
		||||
        // Start off with the current date
 | 
			
		||||
        this._selectedDate = new Date();
 | 
			
		||||
 | 
			
		||||
        this._shouldDateGrabFocus = false;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Table({ homogeneous: false,
 | 
			
		||||
                                    style_class: 'calendar',
 | 
			
		||||
                                    reactive: true });
 | 
			
		||||
@@ -417,21 +420,21 @@ 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() {
 | 
			
		||||
@@ -495,6 +498,7 @@ const Calendar = new Lang.Class({
 | 
			
		||||
            this._onNextMonthButtonClicked();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPrevMonthButtonClicked: function() {
 | 
			
		||||
@@ -518,7 +522,7 @@ const Calendar = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._backButton.grab_key_focus();
 | 
			
		||||
 | 
			
		||||
        this.setDate(newDate, false);
 | 
			
		||||
        this.setDate(newDate);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onNextMonthButtonClicked: function() {
 | 
			
		||||
@@ -542,28 +546,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
 | 
			
		||||
@@ -581,11 +583,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;
 | 
			
		||||
@@ -606,13 +610,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;
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
@@ -620,9 +621,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)
 | 
			
		||||
@@ -639,7 +640,7 @@ 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;
 | 
			
		||||
 | 
			
		||||
@@ -647,12 +648,7 @@ const Calendar = new Lang.Class({
 | 
			
		||||
            this.actor.add(button,
 | 
			
		||||
                           { row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 });
 | 
			
		||||
 | 
			
		||||
            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(),
 | 
			
		||||
@@ -666,9 +662,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');
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -77,7 +77,7 @@ const AutomountManager = new Lang.Class({
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._mountAllId = 0;
 | 
			
		||||
        return false;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDriveConnected: function() {
 | 
			
		||||
@@ -236,7 +236,7 @@ const AutomountManager = new Lang.Class({
 | 
			
		||||
    _allowAutorunExpire: function(volume) {
 | 
			
		||||
        Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() {
 | 
			
		||||
            volume.allowAutorun = false;
 | 
			
		||||
            return false;
 | 
			
		||||
            return GLib.SOURCE_REMOVE;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -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() {
 | 
			
		||||
 
 | 
			
		||||
@@ -45,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' });
 | 
			
		||||
@@ -136,6 +138,7 @@ const KeyringDialog = new Lang.Class({
 | 
			
		||||
        warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE;
 | 
			
		||||
        warning.clutter_text.line_wrap = true;
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -138,6 +138,8 @@ const NetworkSecretDialog = new Lang.Class({
 | 
			
		||||
                           key:    Clutter.KEY_Escape,
 | 
			
		||||
                         },
 | 
			
		||||
                         this._okButton]);
 | 
			
		||||
 | 
			
		||||
        this._updateOkButton();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateOkButton: function() {
 | 
			
		||||
@@ -253,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)
 | 
			
		||||
@@ -306,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':
 | 
			
		||||
@@ -333,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:
 | 
			
		||||
@@ -370,6 +373,12 @@ const VPNRequestHandler = new Lang.Class({
 | 
			
		||||
            argv.push('-i');
 | 
			
		||||
        if (flags & NMClient.SecretAgentGetSecretsFlags.REQUEST_NEW)
 | 
			
		||||
            argv.push('-r');
 | 
			
		||||
        if (authHelper.supportsHints) {
 | 
			
		||||
            for (let i = 0; i < hints.length; i++) {
 | 
			
		||||
                argv.push('-t');
 | 
			
		||||
                argv.push(hints[i]);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._newStylePlugin = authHelper.externalUIMode;
 | 
			
		||||
 | 
			
		||||
@@ -432,6 +441,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
 | 
			
		||||
@@ -506,10 +516,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);
 | 
			
		||||
 | 
			
		||||
@@ -542,13 +554,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());
 | 
			
		||||
@@ -582,7 +597,15 @@ const NetworkAgent = new Lang.Class({
 | 
			
		||||
    Name: 'NetworkAgent',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._native = new Shell.NetworkAgent({ identifier: 'org.gnome.Shell.NetworkAgent' });
 | 
			
		||||
        try {
 | 
			
		||||
            this._native = new Shell.NetworkAgent({ identifier: 'org.gnome.Shell.NetworkAgent',
 | 
			
		||||
                                                    capabilities: NMClient.SecretAgentCapabilities.VPN_HINTS
 | 
			
		||||
                                                  });
 | 
			
		||||
        } catch(e) {
 | 
			
		||||
            // Support older versions without NetworkAgent:capabilities
 | 
			
		||||
            this._native = new Shell.NetworkAgent({ identifier: 'org.gnome.Shell.NetworkAgent'
 | 
			
		||||
                                                  });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._dialogs = { };
 | 
			
		||||
        this._vpnRequests = { };
 | 
			
		||||
@@ -682,16 +705,23 @@ const NetworkAgent = new Lang.Class({
 | 
			
		||||
                    let service = keyfile.get_string('VPN Connection', 'service');
 | 
			
		||||
                    let binary = keyfile.get_string('GNOME', 'auth-dialog');
 | 
			
		||||
                    let externalUIMode = false;
 | 
			
		||||
                    let hints = false;
 | 
			
		||||
 | 
			
		||||
                    try {
 | 
			
		||||
                        externalUIMode = keyfile.get_boolean('GNOME', 'supports-external-ui-mode');
 | 
			
		||||
                    } catch(e) { } // ignore errors if key does not exist
 | 
			
		||||
 | 
			
		||||
                    try {
 | 
			
		||||
                        hints = keyfile.get_boolean('GNOME', 'supports-hints');
 | 
			
		||||
                    } catch(e) { } // ignore errors if key does not exist
 | 
			
		||||
 | 
			
		||||
                    let path = binary;
 | 
			
		||||
                    if (!GLib.path_is_absolute(path)) {
 | 
			
		||||
                        path = GLib.build_filenamev([Config.LIBEXECDIR, path]);
 | 
			
		||||
                    }
 | 
			
		||||
 | 
			
		||||
                    if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE))
 | 
			
		||||
                        this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode };
 | 
			
		||||
                        this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode, supportsHints: hints };
 | 
			
		||||
                    else
 | 
			
		||||
                        throw new Error('VPN plugin at %s is not executable'.format(path));
 | 
			
		||||
                } catch(e) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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,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;
 | 
			
		||||
@@ -28,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'
 | 
			
		||||
@@ -446,6 +449,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;
 | 
			
		||||
 | 
			
		||||
@@ -544,20 +548,19 @@ const ChatSource = new Lang.Class({
 | 
			
		||||
        this._notification.update(this._notification.title, null, { customContent: true });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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() {
 | 
			
		||||
@@ -621,7 +624,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);
 | 
			
		||||
@@ -631,7 +638,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 */
 | 
			
		||||
@@ -675,7 +689,7 @@ const ChatSource = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._notifyTimeoutId = 0;
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // This is called for both messages we send from
 | 
			
		||||
@@ -894,14 +908,14 @@ 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.addActor(emptyLine);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._lastGroupActor.add(body, props.childProps);
 | 
			
		||||
        this._lastMessageBox = new St.BoxLayout({ vertical: false });
 | 
			
		||||
        this._lastMessageBox.add(body, props.childProps);
 | 
			
		||||
        this.addActor(this._lastMessageBox);
 | 
			
		||||
 | 
			
		||||
        this.updated();
 | 
			
		||||
 | 
			
		||||
@@ -930,50 +944,102 @@ 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);
 | 
			
		||||
 | 
			
		||||
        } 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>");
 | 
			
		||||
        } 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> ");
 | 
			
		||||
        }
 | 
			
		||||
        switch (clockFormat) {
 | 
			
		||||
            case '24h':
 | 
			
		||||
                // 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");
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
        default:
 | 
			
		||||
            /* explicit fall-through */
 | 
			
		||||
            case '12h':
 | 
			
		||||
                // 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");
 | 
			
		||||
                }
 | 
			
		||||
                break;
 | 
			
		||||
        }
 | 
			
		||||
        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) {
 | 
			
		||||
@@ -1011,7 +1077,7 @@ const ChatNotification = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.source.setChatState(Tp.ChannelChatState.PAUSED);
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onEntryChanged: function() {
 | 
			
		||||
@@ -1095,22 +1161,16 @@ const RoomInviteNotification = new Lang.Class({
 | 
			
		||||
         * 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.addAction(_("Decline"), Lang.bind(this, function() {
 | 
			
		||||
            dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, '', function(src, result) {
 | 
			
		||||
                src.leave_channels_finish(result);
 | 
			
		||||
            });
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
        this.addAction(_("Accept"), Lang.bind(this, function() {
 | 
			
		||||
            dispatchOp.handle_with_time_async('', global.get_current_time(), function(src, result) {
 | 
			
		||||
                src.handle_with_time_finish(result);
 | 
			
		||||
            });
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
@@ -1136,23 +1196,17 @@ const AudioVideoNotification = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.setUrgency(MessageTray.Urgency.CRITICAL);
 | 
			
		||||
 | 
			
		||||
        this.addButton('reject', _("Decline"));
 | 
			
		||||
        this.addAction(_("Decline"), Lang.bind(this, function() {
 | 
			
		||||
            dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, '', function(src, result) {
 | 
			
		||||
                src.leave_channels_finish(result);
 | 
			
		||||
            });
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
        /* 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.addAction(_("Answer"), Lang.bind(this, function() {
 | 
			
		||||
            dispatchOp.handle_with_time_async('', global.get_current_time(), function(src, result) {
 | 
			
		||||
                src.handle_with_time_finish(result);
 | 
			
		||||
            });
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
@@ -1176,22 +1230,16 @@ const FileTransferNotification = new Lang.Class({
 | 
			
		||||
                    { 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.addAction(_("Decline"), Lang.bind(this, function() {
 | 
			
		||||
            dispatchOp.leave_channels_async(Tp.ChannelGroupChangeReason.NONE, '', function(src, result) {
 | 
			
		||||
                src.leave_channels_finish(result);
 | 
			
		||||
            });
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
        this.addAction(_("Accept"), Lang.bind(this, function() {
 | 
			
		||||
            dispatchOp.handle_with_time_async('', global.get_current_time(), function(src, result) {
 | 
			
		||||
                src.handle_with_time_finish(result);
 | 
			
		||||
            });
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
@@ -1222,7 +1270,8 @@ const SubscriptionRequestNotification = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (file) {
 | 
			
		||||
            let uri = file.get_uri();
 | 
			
		||||
            iconBox.child = textureCache.load_uri_async(uri, iconBox._size, iconBox._size);
 | 
			
		||||
            let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
 | 
			
		||||
            iconBox.child = textureCache.load_uri_async(uri, iconBox._size, iconBox._size, scaleFactor);
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            iconBox.child = new St.Icon({ icon_name: 'avatar-default',
 | 
			
		||||
@@ -1239,27 +1288,20 @@ const SubscriptionRequestNotification = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.addActor(layout);
 | 
			
		||||
 | 
			
		||||
        this.addButton('decline', _("Decline"));
 | 
			
		||||
        this.addButton('accept', _("Accept"));
 | 
			
		||||
        this.addAction(_("Decline"), Lang.bind(this, function() {
 | 
			
		||||
            contact.remove_async(function(src, result) {
 | 
			
		||||
                src.remove_finish(result);
 | 
			
		||||
            });
 | 
			
		||||
        }));
 | 
			
		||||
        this.addAction(_("Accept"), Lang.bind(this, function() {
 | 
			
		||||
            // Authorize the contact and request to see his status as well
 | 
			
		||||
            contact.authorize_publication_async(function(src, result) {
 | 
			
		||||
                src.authorize_publication_finish(result);
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
        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
 | 
			
		||||
            contact.request_subscription_async('', function(src, result) {
 | 
			
		||||
                src.request_subscription_finish(result);
 | 
			
		||||
            });
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._changedId = contact.connect('subscription-states-changed',
 | 
			
		||||
@@ -1358,18 +1400,11 @@ const AccountNotification = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        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.addAction(_("View account"), Lang.bind(this, function() {
 | 
			
		||||
            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(0, -1));
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._enabledId = account.connect('notify::enabled',
 | 
			
		||||
@@ -1387,7 +1422,12 @@ const AccountNotification = new Lang.Class({
 | 
			
		||||
                if (status == Tp.ConnectionStatus.CONNECTED) {
 | 
			
		||||
                    this.destroy();
 | 
			
		||||
                } else if (status == Tp.ConnectionStatus.DISCONNECTED) {
 | 
			
		||||
                    this.update(this.title, this._getMessage(account.connection_error));
 | 
			
		||||
                    let connectionError = account.connection_error;
 | 
			
		||||
 | 
			
		||||
                    if (connectionError == Tp.error_get_dbus_name(Tp.Error.CANCELLED))
 | 
			
		||||
                        this.destroy();
 | 
			
		||||
                    else
 | 
			
		||||
                        this.update(this.title, this._getMessage(connectionError));
 | 
			
		||||
                }
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 
 | 
			
		||||
@@ -165,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',
 | 
			
		||||
 | 
			
		||||
@@ -576,7 +579,8 @@ const Dash = new Lang.Class({
 | 
			
		||||
                    Lang.bind(this, function() {
 | 
			
		||||
                        this._labelShowing = true;
 | 
			
		||||
                        item.showLabel();
 | 
			
		||||
                        return false;
 | 
			
		||||
                        this._showLabelTimeoutId = 0;
 | 
			
		||||
                        return GLib.SOURCE_REMOVE;
 | 
			
		||||
                    }));
 | 
			
		||||
                if (this._resetHoverTimeoutId > 0) {
 | 
			
		||||
                    Mainloop.source_remove(this._resetHoverTimeoutId);
 | 
			
		||||
@@ -592,7 +596,8 @@ 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;
 | 
			
		||||
                    }));
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -629,25 +634,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)
 | 
			
		||||
 
 | 
			
		||||
@@ -63,9 +63,14 @@ const DateMenuButton = new Lang.Class({
 | 
			
		||||
        hbox.add(vbox);
 | 
			
		||||
 | 
			
		||||
        // Date
 | 
			
		||||
        this._date = new St.Label({ style_class: 'datemenu-date-label',
 | 
			
		||||
                                    can_focus: true });
 | 
			
		||||
        vbox.add(this._date);
 | 
			
		||||
        this._date = new St.Button({ style_class: 'datemenu-date-label',
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                   });
 | 
			
		||||
        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();
 | 
			
		||||
@@ -113,22 +118,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);
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
@@ -201,7 +191,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() {
 | 
			
		||||
@@ -217,7 +207,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() {
 | 
			
		||||
@@ -226,7 +216,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() {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										31
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							
							
						
						
									
										31
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							@@ -106,10 +106,10 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _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();
 | 
			
		||||
@@ -118,7 +118,7 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
        this._dragStartX = stageX;
 | 
			
		||||
        this._dragStartY = stageY;
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _grabActor: function() {
 | 
			
		||||
@@ -164,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
 | 
			
		||||
@@ -184,11 +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;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -236,7 +236,7 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
@@ -285,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);
 | 
			
		||||
        }
 | 
			
		||||
@@ -345,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 = {
 | 
			
		||||
@@ -360,7 +362,7 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
                let result = motionFunc(dragEvent);
 | 
			
		||||
                if (result != DragMotionResult.CONTINUE) {
 | 
			
		||||
                    global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
 | 
			
		||||
                    return false;
 | 
			
		||||
                    return GLib.SOURCE_REMOVE;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -378,18 +380,18 @@ const _Draggable = new Lang.Class({
 | 
			
		||||
                                                             0);
 | 
			
		||||
                if (result != DragMotionResult.CONTINUE) {
 | 
			
		||||
                    global.screen.set_cursor(DRAG_CURSOR_MAP[result]);
 | 
			
		||||
                    return false;
 | 
			
		||||
                    return GLib.SOURCE_REMOVE;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            target = target.get_parent();
 | 
			
		||||
        }
 | 
			
		||||
        global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG);
 | 
			
		||||
        return false;
 | 
			
		||||
        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));
 | 
			
		||||
@@ -555,7 +557,8 @@ 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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -27,9 +25,11 @@ 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 LoginManager = imports.misc.loginManager;
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
@@ -38,25 +38,29 @@ 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"),
 | 
			
		||||
@@ -71,6 +75,7 @@ const logoutDialogContent = {
 | 
			
		||||
                        "You will be logged out automatically in %d seconds.",
 | 
			
		||||
                        seconds).format(seconds);
 | 
			
		||||
    },
 | 
			
		||||
    showBatteryWarning: false,
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedLogout',
 | 
			
		||||
                       label:  C_("button", "Log Out") }],
 | 
			
		||||
    iconStyleClass: 'end-session-dialog-logout-icon',
 | 
			
		||||
@@ -79,11 +84,14 @@ const logoutDialogContent = {
 | 
			
		||||
 | 
			
		||||
const shutdownDialogContent = {
 | 
			
		||||
    subject: C_("title", "Power Off"),
 | 
			
		||||
    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);
 | 
			
		||||
    },
 | 
			
		||||
    checkBoxText: C_("checkbox", "Install pending software updates"),
 | 
			
		||||
    showBatteryWarning: true,
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedReboot',
 | 
			
		||||
                       label:  C_("button", "Restart") },
 | 
			
		||||
                     { signal: 'ConfirmedShutdown',
 | 
			
		||||
@@ -100,6 +108,7 @@ const restartDialogContent = {
 | 
			
		||||
                        "The system will restart automatically in %d seconds.",
 | 
			
		||||
                        seconds).format(seconds);
 | 
			
		||||
    },
 | 
			
		||||
    showBatteryWarning: false,
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedReboot',
 | 
			
		||||
                       label:  C_("button", "Restart") }],
 | 
			
		||||
    iconName: 'view-refresh-symbolic',
 | 
			
		||||
@@ -115,8 +124,11 @@ const restartInstallDialogContent = {
 | 
			
		||||
                        "The system will automatically restart and install updates in %d seconds.",
 | 
			
		||||
                        seconds).format(seconds);
 | 
			
		||||
    },
 | 
			
		||||
    showBatteryWarning: true,
 | 
			
		||||
    confirmButtons: [{ signal: 'ConfirmedReboot',
 | 
			
		||||
                       label:  C_("button", "Restart & Install") }],
 | 
			
		||||
                       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,
 | 
			
		||||
@@ -131,16 +143,26 @@ const DialogContent = {
 | 
			
		||||
 | 
			
		||||
const MAX_USERS_IN_SESSION_DIALOG = 5;
 | 
			
		||||
 | 
			
		||||
const LogindSessionIface = <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>;
 | 
			
		||||
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;
 | 
			
		||||
    try {
 | 
			
		||||
@@ -193,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
 | 
			
		||||
@@ -212,6 +246,20 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
        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;
 | 
			
		||||
@@ -238,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' });
 | 
			
		||||
@@ -256,6 +307,16 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
                          { y_fill:  true,
 | 
			
		||||
                            y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        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,
 | 
			
		||||
@@ -269,16 +330,24 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        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({ vertical: true });
 | 
			
		||||
        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._sessionHeader = new St.Label({ style_class: 'end-session-dialog-list-header',
 | 
			
		||||
                                             text: _("Other users are logged in.") });
 | 
			
		||||
        this._sessionList = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        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');
 | 
			
		||||
    },
 | 
			
		||||
@@ -293,13 +362,22 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
        if (!open)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._type == 2 && this._updatesFile.query_exists(null))
 | 
			
		||||
            this._type = 3;
 | 
			
		||||
 | 
			
		||||
        let dialogContent = DialogContent[this._type];
 | 
			
		||||
 | 
			
		||||
        let subject = dialogContent.subject;
 | 
			
		||||
 | 
			
		||||
        // Use different title when we are installing updates
 | 
			
		||||
        if (dialogContent.subjectWithUpdates && this._checkBox.actor.checked)
 | 
			
		||||
            subject = dialogContent.subjectWithUpdates;
 | 
			
		||||
 | 
			
		||||
        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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let description;
 | 
			
		||||
        let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen,
 | 
			
		||||
                                                  this._secondsLeft,
 | 
			
		||||
@@ -382,15 +460,75 @@ 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() {
 | 
			
		||||
        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;
 | 
			
		||||
@@ -403,14 +541,15 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
                this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed;
 | 
			
		||||
                if (this._secondsLeft > 0) {
 | 
			
		||||
                    this._sync();
 | 
			
		||||
                    return true;
 | 
			
		||||
                    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;
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -537,6 +676,9 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
        this._totalSecondsToStayOpen = totalSecondsToStayOpen;
 | 
			
		||||
        this._type = type;
 | 
			
		||||
 | 
			
		||||
        if (this._type == 2 && this._updatesFile.query_exists(null))
 | 
			
		||||
            this._type = 3;
 | 
			
		||||
 | 
			
		||||
        this._applications = [];
 | 
			
		||||
        this._applicationList.destroy_all_children();
 | 
			
		||||
 | 
			
		||||
@@ -549,6 +691,8 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
            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);
 | 
			
		||||
@@ -557,9 +701,23 @@ const EndSessionDialog = new Lang.Class({
 | 
			
		||||
            this._applications.push(inhibitor);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (DialogContent[type].showOtherSessions)
 | 
			
		||||
        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)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -5,6 +5,8 @@ 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;
 | 
			
		||||
 
 | 
			
		||||
@@ -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();
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -32,9 +32,11 @@ 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._atspiInited = false;
 | 
			
		||||
        this._focusListenerRegistered = false;
 | 
			
		||||
        this._caretListenerRegistered = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onChanged: function(event) {
 | 
			
		||||
@@ -44,22 +46,50 @@ const FocusCaretTracker = new Lang.Class({
 | 
			
		||||
            this.emit('caret-moved', event);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _initAtspi: function() {
 | 
			
		||||
        if (!this._atspiInited) {
 | 
			
		||||
            Atspi.init();
 | 
			
		||||
            Atspi.set_timeout(250, 250);
 | 
			
		||||
            this._atspiInited = true;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    registerFocusListener: function() {
 | 
			
		||||
        return this._atspiListener.register(STATECHANGED + ':focused') &&
 | 
			
		||||
               this._atspiListener.register(STATECHANGED + ':selected');
 | 
			
		||||
        if (this._focusListenerRegistered)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._initAtspi();
 | 
			
		||||
 | 
			
		||||
        this._atspiListener.register(STATECHANGED + ':focused');
 | 
			
		||||
        this._atspiListener.register(STATECHANGED + ':selected');
 | 
			
		||||
        this._focusListenerRegistered = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    registerCaretListener: function() {
 | 
			
		||||
        return this._atspiListener.register(CARETMOVED);
 | 
			
		||||
        if (this._caretListenerRegistered)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._initAtspi();
 | 
			
		||||
 | 
			
		||||
        this._atspiListener.register(CARETMOVED);
 | 
			
		||||
        this._caretListenerRegistered = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    deregisterFocusListener: function() {
 | 
			
		||||
        return this._atspiListener.deregister(STATECHANGED + ':focused') &&
 | 
			
		||||
               this._atspiListener.deregister(STATECHANGED + ':selected');
 | 
			
		||||
        if (!this._focusListenerRegistered)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._atspiListener.deregister(STATECHANGED + ':focused');
 | 
			
		||||
        this._atspiListener.deregister(STATECHANGED + ':selected');
 | 
			
		||||
        this._focusListenerRegistered = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    deregisterCaretListener: function() {
 | 
			
		||||
        return this._atspiListener.deregister(CARETMOVED);
 | 
			
		||||
        if (!this._caretListenerRegistered)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._atspiListener.deregister(CARETMOVED);
 | 
			
		||||
        this._caretListenerRegistered = false;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(FocusCaretTracker.prototype);
 | 
			
		||||
 
 | 
			
		||||
@@ -280,7 +280,7 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
        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;
 | 
			
		||||
@@ -289,14 +289,14 @@ const GrabHelper = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (release && this._ignoreRelease) {
 | 
			
		||||
            this._ignoreRelease = false;
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        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,
 | 
			
		||||
@@ -305,9 +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 true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -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 {
 | 
			
		||||
 
 | 
			
		||||
@@ -143,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() {
 | 
			
		||||
@@ -219,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) {
 | 
			
		||||
@@ -413,15 +422,18 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeAll: function() {
 | 
			
		||||
        this._items = [];
 | 
			
		||||
        this._grid.remove_all_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroyAll: function() {
 | 
			
		||||
        this._items = [];
 | 
			
		||||
        this._grid.destroy_all_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    addItem: function(item, index) {
 | 
			
		||||
        if (!item.icon || !item.icon instanceof BaseIcon) {
 | 
			
		||||
            log('Only items with a BaseIcon icon property can be added to IconGrid');
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        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)
 | 
			
		||||
@@ -430,6 +442,10 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
            this._grid.add_actor(item.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeItem: function(item) {
 | 
			
		||||
        this._grid.remove_child(item.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getItemAtIndex: function(index) {
 | 
			
		||||
        return this._grid.get_child_at_index(index);
 | 
			
		||||
    },
 | 
			
		||||
@@ -525,6 +541,7 @@ const IconGrid = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(IconGrid.prototype);
 | 
			
		||||
 | 
			
		||||
const PaginatedIconGrid = new Lang.Class({
 | 
			
		||||
    Name: 'PaginatedIconGrid',
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
@@ -250,7 +268,10 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (!this._showIdleId)
 | 
			
		||||
            this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE,
 | 
			
		||||
                                             Lang.bind(this, function() { this.Show(time); }));
 | 
			
		||||
                                             Lang.bind(this, function() {
 | 
			
		||||
                                                 this.Show(time);
 | 
			
		||||
                                                 return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                             }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createLayersForGroup: function (gname) {
 | 
			
		||||
@@ -292,7 +313,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 +457,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,6 +498,7 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
                                                   Lang.bind(this, function() {
 | 
			
		||||
                                                       this._clearKeyboardRestTimer();
 | 
			
		||||
                                                       this._show(monitor);
 | 
			
		||||
                                                       return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                                   }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -503,6 +524,7 @@ const Keyboard = new Lang.Class({
 | 
			
		||||
                                                   Lang.bind(this, function() {
 | 
			
		||||
                                                       this._clearKeyboardRestTimer();
 | 
			
		||||
                                                       this._hide();
 | 
			
		||||
                                                       return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                                   }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										132
									
								
								js/ui/layout.js
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								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;
 | 
			
		||||
@@ -213,12 +212,21 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        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,
 | 
			
		||||
                                              track_hover: true });
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
@@ -352,26 +360,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[monitorIndex] = 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,
 | 
			
		||||
@@ -381,10 +389,6 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createPrimaryBackground: function() {
 | 
			
		||||
        this._createBackground(this.primaryIndex);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateBackgrounds: function() {
 | 
			
		||||
        let i;
 | 
			
		||||
        for (i = 0; i < this._bgManagers.length; i++)
 | 
			
		||||
@@ -395,11 +399,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();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -595,7 +600,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        if (Main.sessionMode.isGreeter) {
 | 
			
		||||
            this.panelBox.translation_y = -this.panelBox.height;
 | 
			
		||||
        } else {
 | 
			
		||||
            this._createPrimaryBackground();
 | 
			
		||||
            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.
 | 
			
		||||
@@ -610,7 +615,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);
 | 
			
		||||
        }
 | 
			
		||||
@@ -625,7 +630,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        // when the system is bogged down
 | 
			
		||||
        GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() {
 | 
			
		||||
            this._startupAnimation();
 | 
			
		||||
            return false;
 | 
			
		||||
            return GLib.SOURCE_REMOVE;
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -673,7 +678,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
        this.keyboardBox.show();
 | 
			
		||||
 | 
			
		||||
        if (!Main.sessionMode.isGreeter) {
 | 
			
		||||
            this._createSecondaryBackgrounds();
 | 
			
		||||
            this._showSecondaryBackgrounds();
 | 
			
		||||
            global.window_group.remove_clip();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -723,6 +728,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
 | 
			
		||||
@@ -814,13 +834,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.
 | 
			
		||||
 | 
			
		||||
@@ -838,22 +857,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;
 | 
			
		||||
 | 
			
		||||
@@ -864,8 +872,6 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
            let actorData = this._trackedActors[i], visible;
 | 
			
		||||
            if (!actorData.trackFullscreen)
 | 
			
		||||
                continue;
 | 
			
		||||
            if (!actorData.isToplevel)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            if (!windowsVisible)
 | 
			
		||||
                visible = true;
 | 
			
		||||
@@ -905,8 +911,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) {
 | 
			
		||||
@@ -937,7 +943,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;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1011,19 +1017,39 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
                    continue;
 | 
			
		||||
 | 
			
		||||
                // Ensure that the strut rects goes all the way to the screen edge,
 | 
			
		||||
                // as this really what mutter expects.
 | 
			
		||||
                // as this really what mutter expects. However skip this step
 | 
			
		||||
                // in cases where this would render an entire monitor unusable.
 | 
			
		||||
                switch (side) {
 | 
			
		||||
                case Meta.Side.TOP:
 | 
			
		||||
                    y1 = 0;
 | 
			
		||||
                    let hasMonitorsAbove = this.monitors.some(Lang.bind(this,
 | 
			
		||||
                        function(mon) {
 | 
			
		||||
                            return this._isAboveOrBelowPrimary(mon) &&
 | 
			
		||||
                                   mon.y < primary.y;
 | 
			
		||||
                        }));
 | 
			
		||||
                    if (!hasMonitorsAbove)
 | 
			
		||||
                        y1 = 0;
 | 
			
		||||
                    break;
 | 
			
		||||
                case Meta.Side.BOTTOM:
 | 
			
		||||
                    y2 = global.screen_height;
 | 
			
		||||
                    if (this.primaryIndex == this.bottomIndex)
 | 
			
		||||
                        y2 = global.screen_height;
 | 
			
		||||
                    break;
 | 
			
		||||
                case Meta.Side.LEFT:
 | 
			
		||||
                    x1 = 0;
 | 
			
		||||
                    let hasMonitorsLeft = this.monitors.some(Lang.bind(this,
 | 
			
		||||
                        function(mon) {
 | 
			
		||||
                            return !this._isAboveOrBelowPrimary(mon) &&
 | 
			
		||||
                                   mon.x < primary.x;
 | 
			
		||||
                        }));
 | 
			
		||||
                    if (!hasMonitorsLeft)
 | 
			
		||||
                        x1 = 0;
 | 
			
		||||
                    break;
 | 
			
		||||
                case Meta.Side.RIGHT:
 | 
			
		||||
                    x2 = global.screen_width;
 | 
			
		||||
                    let hasMonitorsRight = this.monitors.some(Lang.bind(this,
 | 
			
		||||
                        function(mon) {
 | 
			
		||||
                            return !this._isAboveOrBelowPrimary(mon) &&
 | 
			
		||||
                                   mon.x > primary.x;
 | 
			
		||||
                        }));
 | 
			
		||||
                    if (!hasMonitorsRight)
 | 
			
		||||
                        x2 = global.screen_width;
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
@@ -1042,7 +1068,7 @@ const LayoutManager = new Lang.Class({
 | 
			
		||||
            workspace.set_builtin_struts(struts);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(LayoutManager.prototype);
 | 
			
		||||
@@ -1229,20 +1255,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:
 | 
			
		||||
@@ -43,15 +99,23 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
                                        width: null,
 | 
			
		||||
                                        height: null,
 | 
			
		||||
                                        fadeFactor: DEFAULT_FADE_FACTOR,
 | 
			
		||||
                                        radialEffect: false,
 | 
			
		||||
                                      });
 | 
			
		||||
 | 
			
		||||
        this._container = container;
 | 
			
		||||
        this._children = container.get_children();
 | 
			
		||||
        this._fadeFactor = params.fadeFactor;
 | 
			
		||||
        this.actor = new St.Bin({ x: 0,
 | 
			
		||||
                                  y: 0,
 | 
			
		||||
                                  style_class: 'lightbox',
 | 
			
		||||
                                  reactive: params.inhibitEvents });
 | 
			
		||||
        this._radialEffect = Clutter.feature_available(Clutter.FeatureFlags.SHADERS_GLSL) && params.radialEffect;
 | 
			
		||||
        if (this._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,9 +165,18 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
        fadeInTime = fadeInTime || 0;
 | 
			
		||||
 | 
			
		||||
        Tweener.removeTweens(this.actor);
 | 
			
		||||
        if (fadeInTime != 0) {
 | 
			
		||||
            this.shown = false;
 | 
			
		||||
            this.actor.opacity = 0;
 | 
			
		||||
        if (this._radialEffect) {
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { brightness: VIGNETTE_BRIGHTNESS,
 | 
			
		||||
                               vignetteSharpness: VIGNETTE_SHARPNESS,
 | 
			
		||||
                               time: fadeInTime,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                   this.shown = true;
 | 
			
		||||
                                   this.emit('shown');
 | 
			
		||||
                               })
 | 
			
		||||
                             });
 | 
			
		||||
        } else {
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { opacity: 255 * this._fadeFactor,
 | 
			
		||||
                               time: fadeInTime,
 | 
			
		||||
@@ -113,11 +186,8 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
                                   this.emit('shown');
 | 
			
		||||
                               })
 | 
			
		||||
                             });
 | 
			
		||||
        } else {
 | 
			
		||||
            this.actor.opacity = 255 * this._fadeFactor;
 | 
			
		||||
            this.shown = true;
 | 
			
		||||
            this.emit('shown');
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -126,7 +196,18 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.shown = false;
 | 
			
		||||
        Tweener.removeTweens(this.actor);
 | 
			
		||||
        if (fadeOutTime != 0) {
 | 
			
		||||
        if (this._radialEffect) {
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { brightness: 1.0,
 | 
			
		||||
                               vignetteSharpness: 0.0,
 | 
			
		||||
                               opacity: 0,
 | 
			
		||||
                               time: fadeOutTime,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                   this.actor.hide();
 | 
			
		||||
                               })
 | 
			
		||||
                             });
 | 
			
		||||
        } else {
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { opacity: 0,
 | 
			
		||||
                               time: fadeOutTime,
 | 
			
		||||
@@ -135,8 +216,6 @@ const Lightbox = new Lang.Class({
 | 
			
		||||
                                   this.actor.hide();
 | 
			
		||||
                               })
 | 
			
		||||
                             });
 | 
			
		||||
        } else {
 | 
			
		||||
            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();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -877,7 +831,22 @@ 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';
 | 
			
		||||
                Mainloop.source_remove(this._timeoutId);
 | 
			
		||||
                return GLib.SOURCE_REMOVE;
 | 
			
		||||
           }));
 | 
			
		||||
           return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        let notebook = new Notebook();
 | 
			
		||||
@@ -907,9 +876,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);
 | 
			
		||||
 | 
			
		||||
@@ -1102,7 +1068,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) {
 | 
			
		||||
@@ -1112,7 +1078,7 @@ const LookingGlass = new Lang.Class({
 | 
			
		||||
                this._notebook.nextTab();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open : function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -11,6 +11,7 @@ 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;
 | 
			
		||||
@@ -57,20 +58,6 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
        // Magnifier is a manager of ZoomRegions.
 | 
			
		||||
        this._zoomRegions = [];
 | 
			
		||||
 | 
			
		||||
        // Export to dbus.
 | 
			
		||||
        magDBusService = new MagnifierDBus.ShellMagnifier();
 | 
			
		||||
 | 
			
		||||
        let showAtLaunch = this._settingsInit();
 | 
			
		||||
        this.setActive(showAtLaunch);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _initialize: function() {
 | 
			
		||||
        if (this._initialized)
 | 
			
		||||
            return;
 | 
			
		||||
        this._initialized = true;
 | 
			
		||||
 | 
			
		||||
        this._settingsInitLate();
 | 
			
		||||
 | 
			
		||||
        // Create small clutter tree for the magnified mouse.
 | 
			
		||||
        let cursorTracker = Meta.CursorTracker.get_for_screen(global.screen);
 | 
			
		||||
        this._mouseSprite = new Clutter.Texture();
 | 
			
		||||
@@ -86,11 +73,15 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        let aZoomRegion = new ZoomRegion(this, this._cursorRoot);
 | 
			
		||||
        this._zoomRegions.push(aZoomRegion);
 | 
			
		||||
        this._settingsInitRegion(aZoomRegion);
 | 
			
		||||
        let showAtLaunch = this._settingsInit(aZoomRegion);
 | 
			
		||||
        aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse);
 | 
			
		||||
 | 
			
		||||
        cursorTracker.connect('cursor-changed', Lang.bind(this, this._updateMouseSprite));
 | 
			
		||||
        this._cursorTracker = cursorTracker;
 | 
			
		||||
 | 
			
		||||
        // Export to dbus.
 | 
			
		||||
        magDBusService = new MagnifierDBus.ShellMagnifier();
 | 
			
		||||
        this.setActive(showAtLaunch);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -115,20 +106,21 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
     * @activate:   Boolean to activate or de-activate the magnifier.
 | 
			
		||||
     */
 | 
			
		||||
    setActive: function(activate) {
 | 
			
		||||
        if (activate == this.isActive())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (activate)
 | 
			
		||||
            this._initialize();
 | 
			
		||||
        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.
 | 
			
		||||
@@ -448,68 +440,64 @@ const Magnifier = new Lang.Class({
 | 
			
		||||
        this._mouseSprite.set_anchor_point(xHot, yHot);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _settingsInitRegion: function(zoomRegion) {
 | 
			
		||||
        // Mag factor is accurate to two decimal places.
 | 
			
		||||
        let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
 | 
			
		||||
        if (aPref != 0.0)
 | 
			
		||||
            zoomRegion.setMagFactor(aPref, aPref);
 | 
			
		||||
 | 
			
		||||
        aPref = this._settings.get_enum(SCREEN_POSITION_KEY);
 | 
			
		||||
        if (aPref)
 | 
			
		||||
            zoomRegion.setScreenPosition(aPref);
 | 
			
		||||
 | 
			
		||||
        zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
 | 
			
		||||
        zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY));
 | 
			
		||||
 | 
			
		||||
        aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
 | 
			
		||||
        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);
 | 
			
		||||
 | 
			
		||||
        aPref = this._settings.get_double(COLOR_SATURATION_KEY);
 | 
			
		||||
        if (aPref)
 | 
			
		||||
            zoomRegion.setColorSaturation(aPref);
 | 
			
		||||
 | 
			
		||||
        let bc = {};
 | 
			
		||||
        bc.r = this._settings.get_double(BRIGHT_RED_KEY);
 | 
			
		||||
        bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
 | 
			
		||||
        bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
 | 
			
		||||
        zoomRegion.setBrightness(bc);
 | 
			
		||||
 | 
			
		||||
        bc.r = this._settings.get_double(CONTRAST_RED_KEY);
 | 
			
		||||
        bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
 | 
			
		||||
        bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
 | 
			
		||||
        zoomRegion.setContrast(bc);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _settingsInit: function() {
 | 
			
		||||
    _settingsInit: function(zoomRegion) {
 | 
			
		||||
        this._appSettings = new Gio.Settings({ schema: APPLICATIONS_SCHEMA });
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: MAGNIFIER_SCHEMA });
 | 
			
		||||
 | 
			
		||||
        this._appSettings.connect('changed::' + SHOW_KEY, Lang.bind(this, function() {
 | 
			
		||||
            let active = this._appSettings.get_boolean(SHOW_KEY);
 | 
			
		||||
            this.setActive(active);
 | 
			
		||||
        }));
 | 
			
		||||
        if (zoomRegion) {
 | 
			
		||||
            // Mag factor is accurate to two decimal places.
 | 
			
		||||
            let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2));
 | 
			
		||||
            if (aPref != 0.0)
 | 
			
		||||
                zoomRegion.setMagFactor(aPref, aPref);
 | 
			
		||||
 | 
			
		||||
        return this._appSettings.get_boolean(SHOW_KEY);
 | 
			
		||||
    },
 | 
			
		||||
            aPref = this._settings.get_enum(SCREEN_POSITION_KEY);
 | 
			
		||||
            if (aPref)
 | 
			
		||||
                zoomRegion.setScreenPosition(aPref);
 | 
			
		||||
 | 
			
		||||
            zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY));
 | 
			
		||||
            zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY));
 | 
			
		||||
 | 
			
		||||
            aPref = this._settings.get_enum(MOUSE_TRACKING_KEY);
 | 
			
		||||
            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);
 | 
			
		||||
 | 
			
		||||
            aPref = this._settings.get_double(COLOR_SATURATION_KEY);
 | 
			
		||||
            if (aPref)
 | 
			
		||||
                zoomRegion.setColorSaturation(aPref);
 | 
			
		||||
 | 
			
		||||
            let bc = {};
 | 
			
		||||
            bc.r = this._settings.get_double(BRIGHT_RED_KEY);
 | 
			
		||||
            bc.g = this._settings.get_double(BRIGHT_GREEN_KEY);
 | 
			
		||||
            bc.b = this._settings.get_double(BRIGHT_BLUE_KEY);
 | 
			
		||||
            zoomRegion.setBrightness(bc);
 | 
			
		||||
 | 
			
		||||
            bc.r = this._settings.get_double(CONTRAST_RED_KEY);
 | 
			
		||||
            bc.g = this._settings.get_double(CONTRAST_GREEN_KEY);
 | 
			
		||||
            bc.b = this._settings.get_double(CONTRAST_BLUE_KEY);
 | 
			
		||||
            zoomRegion.setContrast(bc);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    _settingsInitLate: function() {
 | 
			
		||||
        let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY);
 | 
			
		||||
        this.addCrosshairs();
 | 
			
		||||
        this.setCrosshairsVisible(showCrosshairs);
 | 
			
		||||
 | 
			
		||||
        this._appSettings.connect('changed::' + SHOW_KEY,
 | 
			
		||||
                                  Lang.bind(this, function() {
 | 
			
		||||
            this.setActive(this._appSettings.get_boolean(SHOW_KEY));
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._settings.connect('changed::' + SCREEN_POSITION_KEY,
 | 
			
		||||
                               Lang.bind(this, this._updateScreenPosition));
 | 
			
		||||
        this._settings.connect('changed::' + MAG_FACTOR_KEY,
 | 
			
		||||
@@ -573,6 +561,7 @@ 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);
 | 
			
		||||
   },
 | 
			
		||||
 | 
			
		||||
    _updateScreenPosition: function() {
 | 
			
		||||
@@ -731,8 +720,16 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        let component = event.source.get_component_iface();
 | 
			
		||||
        if (!component || event.detail1 != 1)
 | 
			
		||||
            return;
 | 
			
		||||
        let extents = component.get_extents(Atspi.CoordType.SCREEN);
 | 
			
		||||
        [this._xFocus, this._yFocus] = [extents.x, extents.y]
 | 
			
		||||
        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();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -740,7 +737,14 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        let text = event.source.get_text_iface();
 | 
			
		||||
        if (!text)
 | 
			
		||||
            return;
 | 
			
		||||
        let extents = text.get_character_extents(text.get_caret_offset(), 0);
 | 
			
		||||
        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();
 | 
			
		||||
    },
 | 
			
		||||
@@ -763,6 +767,9 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
        } else {
 | 
			
		||||
            this._destroyActors();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._syncCaretTracking();
 | 
			
		||||
        this._syncFocusTracking();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -822,10 +829,7 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
     */
 | 
			
		||||
    setFocusTrackingMode: function(mode) {
 | 
			
		||||
        this._focusTrackingMode = mode;
 | 
			
		||||
        if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.NONE)
 | 
			
		||||
            this._focusCaretTracker.deregisterFocusListener();
 | 
			
		||||
        else
 | 
			
		||||
            this._focusCaretTracker.registerFocusListener();
 | 
			
		||||
        this._syncFocusTracking();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -834,10 +838,27 @@ const ZoomRegion = new Lang.Class({
 | 
			
		||||
     */
 | 
			
		||||
    setCaretTrackingMode: function(mode) {
 | 
			
		||||
        this._caretTrackingMode = mode;
 | 
			
		||||
        if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.NONE)
 | 
			
		||||
            this._focusCaretTracker.deregisterCaretListener();
 | 
			
		||||
        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();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
@@ -1178,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
 | 
			
		||||
 
 | 
			
		||||
@@ -9,85 +9,89 @@ 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="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>;
 | 
			
		||||
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="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>;
 | 
			
		||||
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',
 | 
			
		||||
 
 | 
			
		||||
@@ -112,11 +112,6 @@ 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();
 | 
			
		||||
@@ -614,7 +609,7 @@ function queueDeferredWork(workId) {
 | 
			
		||||
        _deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () {
 | 
			
		||||
            _runAllDeferredWork();
 | 
			
		||||
            _deferredTimeoutId = 0;
 | 
			
		||||
            return false;
 | 
			
		||||
            return GLib.SOURCE_REMOVE;
 | 
			
		||||
        });
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -19,7 +19,6 @@ const BoxPointer = imports.ui.boxpointer;
 | 
			
		||||
const CtrlAltTab = imports.ui.ctrlAltTab;
 | 
			
		||||
const GnomeSession = imports.misc.gnomeSession;
 | 
			
		||||
const GrabHelper = imports.ui.grabHelper;
 | 
			
		||||
const Hash = imports.misc.hash;
 | 
			
		||||
const Lightbox = imports.ui.lightbox;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const PointerWatcher = imports.ui.pointerWatcher;
 | 
			
		||||
@@ -76,7 +75,7 @@ const Urgency = {
 | 
			
		||||
    NORMAL: 1,
 | 
			
		||||
    HIGH: 2,
 | 
			
		||||
    CRITICAL: 3
 | 
			
		||||
}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function _fixMarkup(text, allowMarkup) {
 | 
			
		||||
    if (allowMarkup) {
 | 
			
		||||
@@ -187,7 +186,7 @@ const URLHighlighter = new Lang.Class({
 | 
			
		||||
            // The MessageTray doesn't actually hide us, so
 | 
			
		||||
            // we need to check for paint opacities as well.
 | 
			
		||||
            if (!actor.visible || actor.get_paint_opacity() == 0)
 | 
			
		||||
                return false;
 | 
			
		||||
                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
            // Keep Notification.actor from seeing this and taking
 | 
			
		||||
            // a pointer grab, which would block our button-release-event
 | 
			
		||||
@@ -196,7 +195,7 @@ const URLHighlighter = new Lang.Class({
 | 
			
		||||
        }));
 | 
			
		||||
        this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) {
 | 
			
		||||
            if (!actor.visible || actor.get_paint_opacity() == 0)
 | 
			
		||||
                return false;
 | 
			
		||||
                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
            let urlId = this._findUrlAtPos(event);
 | 
			
		||||
            if (urlId != -1) {
 | 
			
		||||
@@ -204,14 +203,14 @@ const URLHighlighter = new Lang.Class({
 | 
			
		||||
                if (url.indexOf(':') == -1)
 | 
			
		||||
                    url = 'http://' + url;
 | 
			
		||||
 | 
			
		||||
                Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context());
 | 
			
		||||
                return true;
 | 
			
		||||
                Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context(0, -1));
 | 
			
		||||
                return Clutter.EVENT_STOP;
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
        }));
 | 
			
		||||
        this.actor.connect('motion-event', Lang.bind(this, function(actor, event) {
 | 
			
		||||
            if (!actor.visible || actor.get_paint_opacity() == 0)
 | 
			
		||||
                return false;
 | 
			
		||||
                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
            let urlId = this._findUrlAtPos(event);
 | 
			
		||||
            if (urlId != -1 && !this._cursorChanged) {
 | 
			
		||||
@@ -221,16 +220,17 @@ const URLHighlighter = new Lang.Class({
 | 
			
		||||
                global.screen.set_cursor(Meta.Cursor.DEFAULT);
 | 
			
		||||
                this._cursorChanged = false;
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
        }));
 | 
			
		||||
        this.actor.connect('leave-event', Lang.bind(this, function() {
 | 
			
		||||
            if (!this.actor.visible || this.actor.get_paint_opacity() == 0)
 | 
			
		||||
                return;
 | 
			
		||||
                return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
            if (this._cursorChanged) {
 | 
			
		||||
                this._cursorChanged = false;
 | 
			
		||||
                global.screen.set_cursor(Meta.Cursor.DEFAULT);
 | 
			
		||||
            }
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -280,10 +280,6 @@ const URLHighlighter = new Lang.Class({
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function strHasSuffix(string, suffix) {
 | 
			
		||||
    return string.substr(-suffix.length) == suffix;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NotificationPolicy:
 | 
			
		||||
// An object that holds all bits of configurable policy related to a notification
 | 
			
		||||
// source, such as whether to play sound or honour the critical bit.
 | 
			
		||||
@@ -503,7 +499,6 @@ const Notification = new Lang.Class({
 | 
			
		||||
        this.focused = false;
 | 
			
		||||
        this.acknowledged = false;
 | 
			
		||||
        this._destroyed = false;
 | 
			
		||||
        this._useActionIcons = false;
 | 
			
		||||
        this._customContent = false;
 | 
			
		||||
        this.bannerBodyText = null;
 | 
			
		||||
        this.bannerBodyMarkup = false;
 | 
			
		||||
@@ -836,17 +831,7 @@ const Notification = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // addButton:
 | 
			
		||||
    // @id: the action ID
 | 
			
		||||
    // @label: the label for the action's button
 | 
			
		||||
    //
 | 
			
		||||
    // Adds a button with the given @label to the notification. All
 | 
			
		||||
    // action buttons will appear in a single row at the bottom of
 | 
			
		||||
    // the notification.
 | 
			
		||||
    //
 | 
			
		||||
    // If the button is clicked, the notification will emit the
 | 
			
		||||
    // %action-invoked signal with @id as a parameter
 | 
			
		||||
    addButton: function(id, label) {
 | 
			
		||||
    addButton: function(button, callback) {
 | 
			
		||||
        if (!this._buttonBox) {
 | 
			
		||||
            let box = new St.BoxLayout({ style_class: 'notification-actions' });
 | 
			
		||||
            this.setActionArea(box, { x_expand: false,
 | 
			
		||||
@@ -858,43 +843,37 @@ const Notification = new Lang.Class({
 | 
			
		||||
            global.focus_manager.add_group(this._buttonBox);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let button = new St.Button({ can_focus: true });
 | 
			
		||||
        button._actionId = id;
 | 
			
		||||
 | 
			
		||||
        let iconName = strHasSuffix(id, '-symbolic') ? id : id + '-symbolic';
 | 
			
		||||
        if (this._useActionIcons && Gtk.IconTheme.get_default().has_icon(iconName)) {
 | 
			
		||||
            button.add_style_class_name('notification-icon-button');
 | 
			
		||||
            button.child = new St.Icon({ icon_name: iconName });
 | 
			
		||||
        } else {
 | 
			
		||||
            button.add_style_class_name('notification-button');
 | 
			
		||||
            button.label = label;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._buttonBox.add(button);
 | 
			
		||||
        button.connect('clicked', Lang.bind(this, this._onActionInvoked, id));
 | 
			
		||||
        button.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
            callback();
 | 
			
		||||
 | 
			
		||||
            if (!this.resident) {
 | 
			
		||||
                // We don't hide a resident notification when the user invokes one of its actions,
 | 
			
		||||
                // because it is common for such notifications to update themselves with new
 | 
			
		||||
                // information based on the action. We'd like to display the updated information
 | 
			
		||||
                // in place, rather than pop-up a new notification.
 | 
			
		||||
                this.emit('done-displaying');
 | 
			
		||||
                this.destroy();
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this.updated();
 | 
			
		||||
        return button;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // setButtonSensitive:
 | 
			
		||||
    // @id: the action ID
 | 
			
		||||
    // @sensitive: whether the button should be sensitive
 | 
			
		||||
    // addAction:
 | 
			
		||||
    // @label: the label for the action's button
 | 
			
		||||
    // @callback: the callback for the action
 | 
			
		||||
    //
 | 
			
		||||
    // If the notification contains a button with action ID @id,
 | 
			
		||||
    // its sensitivity will be set to @sensitive. Insensitive
 | 
			
		||||
    // buttons cannot be clicked.
 | 
			
		||||
    setButtonSensitive: function(id, sensitive) {
 | 
			
		||||
        if (!this._buttonBox)
 | 
			
		||||
            return;
 | 
			
		||||
    // Adds a button with the given @label to the notification. All
 | 
			
		||||
    // action buttons will appear in a single row at the bottom of
 | 
			
		||||
    // the notification.
 | 
			
		||||
    addAction: function(label, callback) {
 | 
			
		||||
        let button = new St.Button({ style_class: 'notification-button',
 | 
			
		||||
                                     label: label,
 | 
			
		||||
                                     can_focus: true });
 | 
			
		||||
 | 
			
		||||
        let button = this._buttonBox.get_children().filter(function(b) {
 | 
			
		||||
            return b._actionId == id;
 | 
			
		||||
        })[0];
 | 
			
		||||
 | 
			
		||||
        if (!button || button.reactive == sensitive)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        button.reactive = sensitive;
 | 
			
		||||
        return this.addButton(button, callback);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setUrgency: function(urgency) {
 | 
			
		||||
@@ -913,10 +892,6 @@ const Notification = new Lang.Class({
 | 
			
		||||
        this.forFeedback = forFeedback;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setUseActionIcons: function(useIcons) {
 | 
			
		||||
        this._useActionIcons = useIcons;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _styleChanged: function() {
 | 
			
		||||
        this._spacing = this._table.get_theme_node().get_length('spacing-columns');
 | 
			
		||||
    },
 | 
			
		||||
@@ -1026,6 +1001,9 @@ const Notification = new Lang.Class({
 | 
			
		||||
            Meta.later_add(Meta.LaterType.BEFORE_REDRAW,
 | 
			
		||||
                           Lang.bind(this,
 | 
			
		||||
                                     function() {
 | 
			
		||||
                                         if (this._destroyed)
 | 
			
		||||
                                             return false;
 | 
			
		||||
 | 
			
		||||
                                        if (this._canExpandContent()) {
 | 
			
		||||
                                            this._addBannerBody();
 | 
			
		||||
                                            this._table.add_style_class_name('multi-line-notification');
 | 
			
		||||
@@ -1141,18 +1119,6 @@ const Notification = new Lang.Class({
 | 
			
		||||
        this.actor.add_style_class_name('notification-unexpanded');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onActionInvoked: function(actor, mouseButtonClicked, id) {
 | 
			
		||||
        this.emit('action-invoked', id);
 | 
			
		||||
        if (!this.resident) {
 | 
			
		||||
            // We don't hide a resident notification when the user invokes one of its actions,
 | 
			
		||||
            // because it is common for such notifications to update themselves with new
 | 
			
		||||
            // information based on the action. We'd like to display the updated information
 | 
			
		||||
            // in place, rather than pop-up a new notification.
 | 
			
		||||
            this.emit('done-displaying');
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onClicked: function() {
 | 
			
		||||
        this.emit('clicked');
 | 
			
		||||
        // We hide all types of notifications once the user clicks on them because the common
 | 
			
		||||
@@ -1196,32 +1162,12 @@ const SourceActor = new Lang.Class({
 | 
			
		||||
        }));
 | 
			
		||||
        this._actorDestroyed = false;
 | 
			
		||||
 | 
			
		||||
        this._counterLabel = new St.Label( {x_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                            x_expand: true,
 | 
			
		||||
                                            y_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                            y_expand: true });
 | 
			
		||||
 | 
			
		||||
        this._counterBin = new St.Bin({ style_class: 'summary-source-counter',
 | 
			
		||||
                                        child: this._counterLabel,
 | 
			
		||||
                                        layout_manager: new Clutter.BinLayout() });
 | 
			
		||||
        this._counterBin.hide();
 | 
			
		||||
 | 
			
		||||
        this._counterBin.connect('style-changed', Lang.bind(this, function() {
 | 
			
		||||
            let themeNode = this._counterBin.get_theme_node();
 | 
			
		||||
            this._counterBin.translation_x = themeNode.get_length('-shell-counter-overlap-x');
 | 
			
		||||
            this._counterBin.translation_y = themeNode.get_length('-shell-counter-overlap-y');
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._iconBin = new St.Bin({ width: size,
 | 
			
		||||
                                     height: size,
 | 
			
		||||
                                     x_fill: true,
 | 
			
		||||
                                     y_fill: true });
 | 
			
		||||
        let scale_factor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
 | 
			
		||||
        this._iconBin = new St.Bin({ x_fill: true,
 | 
			
		||||
                                     height: size * scale_factor,
 | 
			
		||||
                                     width: size * scale_factor });
 | 
			
		||||
 | 
			
		||||
        this.actor.add_actor(this._iconBin);
 | 
			
		||||
        this.actor.add_actor(this._counterBin);
 | 
			
		||||
 | 
			
		||||
        this._source.connect('count-updated', Lang.bind(this, this._updateCount));
 | 
			
		||||
        this._updateCount();
 | 
			
		||||
 | 
			
		||||
        this._source.connect('icon-updated', Lang.bind(this, this._updateIcon));
 | 
			
		||||
        this._updateIcon();
 | 
			
		||||
@@ -1245,6 +1191,48 @@ const SourceActor = new Lang.Class({
 | 
			
		||||
    _allocate: function(actor, box, flags) {
 | 
			
		||||
        // the iconBin should fill our entire box
 | 
			
		||||
        this._iconBin.allocate(box, flags);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateIcon: function() {
 | 
			
		||||
        if (this._actorDestroyed)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!this._iconSet)
 | 
			
		||||
            this._iconBin.child = this._source.createIcon(this._size);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const SourceActorWithLabel = new Lang.Class({
 | 
			
		||||
    Name: 'SourceActorWithLabel',
 | 
			
		||||
    Extends: SourceActor,
 | 
			
		||||
 | 
			
		||||
    _init: function(source, size) {
 | 
			
		||||
        this.parent(source, size);
 | 
			
		||||
 | 
			
		||||
        this._counterLabel = new St.Label({ x_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                            x_expand: true,
 | 
			
		||||
                                            y_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                            y_expand: true });
 | 
			
		||||
 | 
			
		||||
        this._counterBin = new St.Bin({ style_class: 'summary-source-counter',
 | 
			
		||||
                                        child: this._counterLabel,
 | 
			
		||||
                                        layout_manager: new Clutter.BinLayout() });
 | 
			
		||||
        this._counterBin.hide();
 | 
			
		||||
 | 
			
		||||
        this._counterBin.connect('style-changed', Lang.bind(this, function() {
 | 
			
		||||
            let themeNode = this._counterBin.get_theme_node();
 | 
			
		||||
            this._counterBin.translation_x = themeNode.get_length('-shell-counter-overlap-x');
 | 
			
		||||
            this._counterBin.translation_y = themeNode.get_length('-shell-counter-overlap-y');
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this.actor.add_actor(this._counterBin);
 | 
			
		||||
 | 
			
		||||
        this._source.connect('count-updated', Lang.bind(this, this._updateCount));
 | 
			
		||||
        this._updateCount();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _allocate: function(actor, box, flags) {
 | 
			
		||||
        this.parent(actor, box, flags);
 | 
			
		||||
 | 
			
		||||
        let childBox = new Clutter.ActorBox();
 | 
			
		||||
 | 
			
		||||
@@ -1267,14 +1255,6 @@ const SourceActor = new Lang.Class({
 | 
			
		||||
        this._counterBin.allocate(childBox, flags);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateIcon: function() {
 | 
			
		||||
        if (this._actorDestroyed)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (!this._iconSet)
 | 
			
		||||
            this._iconBin.child = this._source.createIcon(this._size);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateCount: function() {
 | 
			
		||||
        if (this._actorDestroyed)
 | 
			
		||||
            return;
 | 
			
		||||
@@ -1300,7 +1280,6 @@ const Source = new Lang.Class({
 | 
			
		||||
        this.title = title;
 | 
			
		||||
        this.iconName = iconName;
 | 
			
		||||
 | 
			
		||||
        this.isTransient = false;
 | 
			
		||||
        this.isChat = false;
 | 
			
		||||
        this.isMuted = false;
 | 
			
		||||
        this.keepTrayOnSummaryClick = false;
 | 
			
		||||
@@ -1360,10 +1339,6 @@ const Source = new Lang.Class({
 | 
			
		||||
        return rightClickMenu;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setTransient: function(isTransient) {
 | 
			
		||||
        this.isTransient = isTransient;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setTitle: function(newTitle) {
 | 
			
		||||
        this.title = newTitle;
 | 
			
		||||
        this.emit('title-changed');
 | 
			
		||||
@@ -1392,7 +1367,7 @@ const Source = new Lang.Class({
 | 
			
		||||
        if (this._mainIcon)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._mainIcon = new SourceActor(this, this.SOURCE_ICON_SIZE);
 | 
			
		||||
        this._mainIcon = new SourceActorWithLabel(this, this.SOURCE_ICON_SIZE);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // Unlike createIcon, this always returns the same actor;
 | 
			
		||||
@@ -1418,9 +1393,7 @@ const Source = new Lang.Class({
 | 
			
		||||
        if (this.notifications.indexOf(notification) >= 0)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        notification.connect('clicked', Lang.bind(this, this.open));
 | 
			
		||||
        notification.connect('destroy', Lang.bind(this, this._onNotificationDestroy));
 | 
			
		||||
 | 
			
		||||
        this.notifications.push(notification);
 | 
			
		||||
        this.emit('notification-added', notification);
 | 
			
		||||
 | 
			
		||||
@@ -1567,9 +1540,9 @@ const SummaryItem = new Lang.Class({
 | 
			
		||||
    _onKeyPress: function(actor, event) {
 | 
			
		||||
        if (event.get_key_symbol() == Clutter.KEY_Up) {
 | 
			
		||||
            actor.emit('clicked', 1);
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    prepareNotificationStackForShowing: function() {
 | 
			
		||||
@@ -1664,7 +1637,7 @@ const MessageTrayMenu = new Lang.Class({
 | 
			
		||||
        this._clearItem = this.addAction(_("Clear Messages"), function() {
 | 
			
		||||
            let toDestroy = tray.getSources().filter(function(source) {
 | 
			
		||||
                return source.isClearable;
 | 
			
		||||
            })
 | 
			
		||||
            });
 | 
			
		||||
 | 
			
		||||
            toDestroy.forEach(function(source) {
 | 
			
		||||
                source.destroy();
 | 
			
		||||
@@ -1827,6 +1800,7 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
            this._setClickedSummaryItem(null);
 | 
			
		||||
            this._updateState();
 | 
			
		||||
            actor.grab_key_focus();
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
        }));
 | 
			
		||||
        global.focus_manager.add_group(this.actor);
 | 
			
		||||
        this._summary = new St.BoxLayout({ style_class: 'message-tray-summary',
 | 
			
		||||
@@ -1941,7 +1915,7 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
                              Shell.KeyBindingMode.OVERVIEW,
 | 
			
		||||
                              Lang.bind(this, this._expandActiveNotification));
 | 
			
		||||
 | 
			
		||||
        this._sources = new Hash.Map();
 | 
			
		||||
        this._sources = new Map();
 | 
			
		||||
        this._chatSummaryItemsCount = 0;
 | 
			
		||||
 | 
			
		||||
        this._trayDwellTimeoutId = 0;
 | 
			
		||||
@@ -1978,7 +1952,7 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateNoMessagesLabel: function() {
 | 
			
		||||
        this._noMessages.visible = this._sources.size() == 0;
 | 
			
		||||
        this._noMessages.visible = this._sources.size == 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sessionUpdated: function() {
 | 
			
		||||
@@ -2032,32 +2006,37 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
        this._trayDwellTimeoutId = 0;
 | 
			
		||||
 | 
			
		||||
        if (Main.layoutManager.bottomMonitor.inFullscreen)
 | 
			
		||||
            return false;
 | 
			
		||||
            return GLib.SOURCE_REMOVE;
 | 
			
		||||
 | 
			
		||||
        // We don't want to open the tray when a modal dialog
 | 
			
		||||
        // is up, so we check the modal count for that. When we are in the
 | 
			
		||||
        // overview we have to take the overview's modal push into account
 | 
			
		||||
        if (Main.modalCount > (Main.overview.visible ? 1 : 0))
 | 
			
		||||
            return false;
 | 
			
		||||
            return GLib.SOURCE_REMOVE;
 | 
			
		||||
 | 
			
		||||
        // If the user interacted with the focus window since we started the tray
 | 
			
		||||
        // dwell (by clicking or typing), don't activate the message tray
 | 
			
		||||
        let focusWindow = global.display.focus_window;
 | 
			
		||||
        let currentUserTime = focusWindow ? focusWindow.user_time : 0;
 | 
			
		||||
        if (currentUserTime != this._trayDwellUserTime)
 | 
			
		||||
            return false;
 | 
			
		||||
            return GLib.SOURCE_REMOVE;
 | 
			
		||||
 | 
			
		||||
        this.openTray();
 | 
			
		||||
        return false;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onNotificationKeyRelease: function(actor, event) {
 | 
			
		||||
        if (event.get_key_symbol() == Clutter.KEY_Escape && event.get_state() == 0) {
 | 
			
		||||
            this._closeNotification();
 | 
			
		||||
            return true;
 | 
			
		||||
            this._expireNotification();
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _expireNotification: function() {
 | 
			
		||||
        this._notificationExpired = true;
 | 
			
		||||
        this._updateState();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _closeNotification: function() {
 | 
			
		||||
@@ -2141,7 +2120,8 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _removeSource: function(source) {
 | 
			
		||||
        let [, obj] = this._sources.delete(source);
 | 
			
		||||
        let obj = this._sources.get(source);
 | 
			
		||||
        this._sources.delete(source);
 | 
			
		||||
        let summaryItem = obj.summaryItem;
 | 
			
		||||
 | 
			
		||||
        if (source.isChat)
 | 
			
		||||
@@ -2162,7 +2142,7 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getSources: function() {
 | 
			
		||||
        return this._sources.keys();
 | 
			
		||||
        return [k for (k of this._sources.keys())];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSourceEnableChanged: function(policy, source) {
 | 
			
		||||
@@ -2212,7 +2192,10 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    toggleAndNavigate: function() {
 | 
			
		||||
        if (this.toggle())
 | 
			
		||||
        if (!this.toggle())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._traySummoned)
 | 
			
		||||
            this._summary.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -2375,7 +2358,7 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
            this._updateNotificationTimeout(0);
 | 
			
		||||
            this._updateState();
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _escapeTray: function() {
 | 
			
		||||
@@ -2392,6 +2375,13 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
    // _updateState() figures out what (if anything) needs to be done
 | 
			
		||||
    // at the present time.
 | 
			
		||||
    _updateState: function() {
 | 
			
		||||
        // If our state changes caused _updateState to be called,
 | 
			
		||||
        // just exit now to prevent reentrancy issues.
 | 
			
		||||
        if (this._updatingState)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._updatingState = true;
 | 
			
		||||
 | 
			
		||||
        // Filter out acknowledged notifications.
 | 
			
		||||
        this._notificationQueue = this._notificationQueue.filter(function(n) {
 | 
			
		||||
            return !n.acknowledged;
 | 
			
		||||
@@ -2409,20 +2399,19 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
                    this._showNotification();
 | 
			
		||||
            }
 | 
			
		||||
        } else if (this._notificationState == State.SHOWN) {
 | 
			
		||||
            let pinned = this._pointerInNotification && !this._notificationRemoved;
 | 
			
		||||
            let expired = (this._userActiveWhileNotificationShown &&
 | 
			
		||||
                           this._notificationTimeoutId == 0 &&
 | 
			
		||||
                           !(this._notification.urgency == Urgency.CRITICAL) &&
 | 
			
		||||
                           this._notification.urgency != Urgency.CRITICAL &&
 | 
			
		||||
                           !this._notification.focused &&
 | 
			
		||||
                           !this._pointerInNotification);
 | 
			
		||||
                           !this._pointerInNotification) || this._notificationExpired;
 | 
			
		||||
            let mustClose = (this._notificationRemoved || !hasNotifications || expired || this._traySummoned);
 | 
			
		||||
 | 
			
		||||
            if (mustClose) {
 | 
			
		||||
                let animate = hasNotifications && !this._notificationRemoved;
 | 
			
		||||
                this._hideNotification(animate);
 | 
			
		||||
            } else if (pinned && !this._notification.expanded) {
 | 
			
		||||
            } else if (this._pointerInNotification && !this._notification.expanded) {
 | 
			
		||||
                this._expandNotification(false);
 | 
			
		||||
            } else if (pinned) {
 | 
			
		||||
            } else if (this._pointerInNotification) {
 | 
			
		||||
                this._ensureNotificationFocused();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
@@ -2469,11 +2458,16 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
                                     this._desktopCloneState == State.SHOWN);
 | 
			
		||||
        let desktopCloneShouldBeVisible = (trayShouldBeVisible);
 | 
			
		||||
 | 
			
		||||
        if (!desktopCloneIsVisible && desktopCloneShouldBeVisible) {
 | 
			
		||||
        if (!desktopCloneIsVisible && desktopCloneShouldBeVisible)
 | 
			
		||||
            this._showDesktopClone();
 | 
			
		||||
        } else if (desktopCloneIsVisible && !desktopCloneShouldBeVisible) {
 | 
			
		||||
        else if (desktopCloneIsVisible && !desktopCloneShouldBeVisible)
 | 
			
		||||
            this._hideDesktopClone();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._updatingState = false;
 | 
			
		||||
 | 
			
		||||
        // Clean transient variables that are used to communicate actions
 | 
			
		||||
        // to updateState()
 | 
			
		||||
        this._notificationExpired = false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _tween: function(actor, statevar, value, params) {
 | 
			
		||||
@@ -2701,7 +2695,7 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._lastSeenMouseX = x;
 | 
			
		||||
        this._lastSeenMouseY = y;
 | 
			
		||||
        return false;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hideNotification: function(animate) {
 | 
			
		||||
@@ -2792,12 +2786,17 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
        } else if (this._notification.y != expandedY) {
 | 
			
		||||
            // Tween also opacity here, to override a possible tween that's
 | 
			
		||||
            // currently hiding the notification.
 | 
			
		||||
            this._tween(this._notificationWidget, '_notificationState', State.SHOWN,
 | 
			
		||||
                        { y: expandedY,
 | 
			
		||||
                          opacity: 255,
 | 
			
		||||
                          time: ANIMATION_TIME,
 | 
			
		||||
                          transition: 'easeOutQuad'
 | 
			
		||||
                        });
 | 
			
		||||
            Tweener.addTween(this._notificationWidget,
 | 
			
		||||
                             { y: expandedY,
 | 
			
		||||
                               opacity: 255,
 | 
			
		||||
                               time: ANIMATION_TIME,
 | 
			
		||||
                               transition: 'easeOutQuad',
 | 
			
		||||
                               // HACK: Drive the state machine here better,
 | 
			
		||||
                               // instead of overwriting tweens
 | 
			
		||||
                               onComplete: Lang.bind(this, function() {
 | 
			
		||||
                                   this._notificationState = State.SHOWN;
 | 
			
		||||
                               }),
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -2838,13 +2837,13 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
                                                                                  Lang.bind(this, this._onSourceDoneDisplayingContent));
 | 
			
		||||
 | 
			
		||||
        this._summaryBoxPointer.bin.child = child;
 | 
			
		||||
        this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
 | 
			
		||||
                                onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
 | 
			
		||||
 | 
			
		||||
        this._summaryBoxPointer.actor.opacity = 0;
 | 
			
		||||
        this._summaryBoxPointer.actor.show();
 | 
			
		||||
        this._adjustSummaryBoxPointerPosition();
 | 
			
		||||
 | 
			
		||||
        this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child,
 | 
			
		||||
                                onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) });
 | 
			
		||||
 | 
			
		||||
        this._summaryBoxPointerState = State.SHOWING;
 | 
			
		||||
        this._summaryBoxPointer.show(BoxPointer.PopupAnimation.FULL, Lang.bind(this, function() {
 | 
			
		||||
            this._summaryBoxPointerState = State.SHOWN;
 | 
			
		||||
@@ -2899,13 +2898,13 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
        case Clutter.KEY_Escape:
 | 
			
		||||
            this._setClickedSummaryItem(null);
 | 
			
		||||
            this._updateState();
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        case Clutter.KEY_Delete:
 | 
			
		||||
            this._clickedSummaryItem.source.destroy();
 | 
			
		||||
            this._escapeTray();
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSummaryBoxPointerUngrabbed: function() {
 | 
			
		||||
@@ -2943,8 +2942,6 @@ const MessageTray = new Lang.Class({
 | 
			
		||||
            this._summaryBoxPointerItem.doneShowingNotificationStack();
 | 
			
		||||
            this._summaryBoxPointerItem = null;
 | 
			
		||||
 | 
			
		||||
            if (source.isTransient && !this._reNotifyAfterHideNotification)
 | 
			
		||||
                source.destroy(NotificationDestroyedReason.EXPIRED);
 | 
			
		||||
            if (this._reNotifyAfterHideNotification) {
 | 
			
		||||
                this._onNotify(this._reNotifyAfterHideNotification.source, this._reNotifyAfterHideNotification);
 | 
			
		||||
                this._reNotifyAfterHideNotification = null;
 | 
			
		||||
@@ -2963,7 +2960,6 @@ const SystemNotificationSource = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent(_("System Information"), 'dialog-information-symbolic');
 | 
			
		||||
        this.setTransient(true);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -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 });
 | 
			
		||||
@@ -89,7 +88,8 @@ const ModalDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        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 });
 | 
			
		||||
@@ -229,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) {
 | 
			
		||||
@@ -237,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() {
 | 
			
		||||
 
 | 
			
		||||
@@ -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,
 | 
			
		||||
@@ -103,11 +106,11 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = {
 | 
			
		||||
    'ibus-ui-gtk': 'keyboard'
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const NotificationDaemon = new Lang.Class({
 | 
			
		||||
    Name: 'NotificationDaemon',
 | 
			
		||||
const FdoNotificationDaemon = new Lang.Class({
 | 
			
		||||
    Name: 'FdoNotificationDaemon',
 | 
			
		||||
 | 
			
		||||
    _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 = [];
 | 
			
		||||
@@ -115,6 +118,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));
 | 
			
		||||
@@ -176,12 +181,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.
 | 
			
		||||
@@ -199,32 +202,20 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
        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;
 | 
			
		||||
@@ -252,11 +243,11 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
              hints['category'] == 'presence.offline')) {
 | 
			
		||||
            // Ignore replacesId since we already sent back a
 | 
			
		||||
            // NotificationClosed for that id.
 | 
			
		||||
            id = nextNotificationId++;
 | 
			
		||||
            id = this._nextNotificationId++;
 | 
			
		||||
            Mainloop.idle_add(Lang.bind(this,
 | 
			
		||||
                                        function () {
 | 
			
		||||
                                            this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED);
 | 
			
		||||
                                            return false;
 | 
			
		||||
                                            return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                        }));
 | 
			
		||||
            return invocation.return_value(GLib.Variant.new('(u)', [id]));
 | 
			
		||||
        }
 | 
			
		||||
@@ -296,7 +287,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;
 | 
			
		||||
 | 
			
		||||
@@ -331,26 +322,29 @@ 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 });
 | 
			
		||||
        let iconName = id.endsWith('-symbolic') ? id : id + '-symbolic';
 | 
			
		||||
        if (useActionIcons && Gtk.IconTheme.get_default().has_icon(iconName)) {
 | 
			
		||||
            button.add_style_class_name('notification-icon-button');
 | 
			
		||||
            button.child = new St.Icon({ icon_name: iconName });
 | 
			
		||||
        } else {
 | 
			
		||||
            button.add_style_class_name('notification-button');
 | 
			
		||||
            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,
 | 
			
		||||
@@ -376,10 +370,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
 | 
			
		||||
@@ -413,18 +403,33 @@ const NotificationDaemon = new Lang.Class({
 | 
			
		||||
                                             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);
 | 
			
		||||
@@ -517,8 +522,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) {
 | 
			
		||||
@@ -553,7 +558,7 @@ 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 MessageTray.NotificationApplicationPolicy(id);
 | 
			
		||||
        } else {
 | 
			
		||||
@@ -643,7 +648,7 @@ const Source = new Lang.Class({
 | 
			
		||||
        this.parent(title);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    open: function(notification) {
 | 
			
		||||
    open: function() {
 | 
			
		||||
        this.openApp();
 | 
			
		||||
        this.destroyNonResidentNotifications();
 | 
			
		||||
    },
 | 
			
		||||
@@ -685,3 +690,290 @@ const Source = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const PRIORITY_URGENCY_MAP = {
 | 
			
		||||
    low: MessageTray.Urgency.LOW,
 | 
			
		||||
    normal: MessageTray.Urgency.NORMAL,
 | 
			
		||||
    high: MessageTray.Urgency.HIGH,
 | 
			
		||||
    urgent: MessageTray.Urgency.CRITICAL
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
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,
 | 
			
		||||
              "priority": priority,
 | 
			
		||||
              "buttons": buttons,
 | 
			
		||||
              "default-action": defaultAction,
 | 
			
		||||
              "default-action-target": defaultActionTarget } = notification;
 | 
			
		||||
 | 
			
		||||
        if (priority) {
 | 
			
		||||
            let urgency = PRIORITY_URGENCY_MAP[priority.unpack()];
 | 
			
		||||
            this.setUrgency(urgency != undefined ? urgency : MessageTray.Urgency.NORMAL);
 | 
			
		||||
        } else if (urgent) {
 | 
			
		||||
            this.setUrgency(urgent.unpack() ? MessageTray.Urgency.CRITICAL
 | 
			
		||||
                            : MessageTray.Urgency.NORMAL);
 | 
			
		||||
        } else {
 | 
			
		||||
            this.setUrgency(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,6 +66,7 @@ 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();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -77,7 +79,8 @@ const OsdWindow = new Lang.Class({
 | 
			
		||||
                                     y_expand: true,
 | 
			
		||||
                                     x_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                     y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true }));
 | 
			
		||||
        this._currentMonitor = undefined;
 | 
			
		||||
        this.setMonitor (-1);
 | 
			
		||||
        this._box = new St.BoxLayout({ style_class: 'osd-window',
 | 
			
		||||
                                       vertical: true });
 | 
			
		||||
        this.actor.add_actor(this._box);
 | 
			
		||||
@@ -122,13 +125,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() {
 | 
			
		||||
@@ -172,6 +177,7 @@ const OsdWindow = new Lang.Class({
 | 
			
		||||
                              Meta.enable_unredirect_for_screen(global.screen);
 | 
			
		||||
                           })
 | 
			
		||||
                         });
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _reset: function() {
 | 
			
		||||
@@ -182,14 +188,21 @@ const OsdWindow = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _monitorsChanged: function() {
 | 
			
		||||
        /* assume 110x110 on a 640x480 display and scale from there */
 | 
			
		||||
        let monitor = Main.layoutManager.primaryMonitor;
 | 
			
		||||
        let monitor;
 | 
			
		||||
 | 
			
		||||
        if (this._currentMonitor >= 0)
 | 
			
		||||
            monitor = Main.layoutManager.monitors[this._currentMonitor];
 | 
			
		||||
        else
 | 
			
		||||
            monitor = Main.layoutManager.primaryMonitor;
 | 
			
		||||
 | 
			
		||||
        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,27 @@ 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);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setMonitor: function(index) {
 | 
			
		||||
        let constraint;
 | 
			
		||||
 | 
			
		||||
        if (index < 0)
 | 
			
		||||
            index = -1;
 | 
			
		||||
        if (this._currentMonitor == index)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (index < 0)
 | 
			
		||||
            constraint = new Layout.MonitorConstraint({ primary: true });
 | 
			
		||||
        else
 | 
			
		||||
            constraint = new Layout.MonitorConstraint({ index: index });
 | 
			
		||||
 | 
			
		||||
        this.actor.clear_constraints();
 | 
			
		||||
        this.actor.add_constraint(constraint);
 | 
			
		||||
        this._currentMonitor = index;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -78,10 +80,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 +114,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 +132,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
 | 
			
		||||
@@ -149,7 +149,7 @@ const Overview = new Lang.Class({
 | 
			
		||||
        this._coverPane = new Clutter.Actor({ opacity: 0,
 | 
			
		||||
                                              reactive: true });
 | 
			
		||||
        Main.layoutManager.overviewGroup.add_child(this._coverPane);
 | 
			
		||||
        this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; }));
 | 
			
		||||
        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 +197,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 +210,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'
 | 
			
		||||
                             });
 | 
			
		||||
@@ -365,11 +357,13 @@ 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;
 | 
			
		||||
                                            }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -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();
 | 
			
		||||
 
 | 
			
		||||
@@ -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);
 | 
			
		||||
@@ -66,9 +67,9 @@ const SlideLayout = new Lang.Class({
 | 
			
		||||
        let alignX = (realDirection == SlideDirection.LEFT) ? (availWidth - natWidth) : 0;
 | 
			
		||||
 | 
			
		||||
        let actorBox = new Clutter.ActorBox();
 | 
			
		||||
        actorBox.x1 = alignX;
 | 
			
		||||
        actorBox.x2 = actorBox.x1 + child.x_expand ? availWidth : natWidth;
 | 
			
		||||
        actorBox.y1 = 0;
 | 
			
		||||
        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);
 | 
			
		||||
@@ -90,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({
 | 
			
		||||
@@ -99,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;
 | 
			
		||||
@@ -109,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));
 | 
			
		||||
@@ -119,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' });
 | 
			
		||||
    },
 | 
			
		||||
@@ -151,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() {
 | 
			
		||||
@@ -184,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() {
 | 
			
		||||
@@ -209,13 +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
 | 
			
		||||
    },
 | 
			
		||||
@@ -225,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();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -244,16 +257,15 @@ const ThumbnailsSlider = new Lang.Class({
 | 
			
		||||
        this.actor.track_hover = true;
 | 
			
		||||
        this.actor.add_actor(this._thumbnailsBox.actor);
 | 
			
		||||
 | 
			
		||||
        Main.layoutManager.connect('monitors-changed', Lang.bind(this, this.updateSlide));
 | 
			
		||||
        Main.overview.connect('hiding', Lang.bind(this, this.slideOut));
 | 
			
		||||
        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));
 | 
			
		||||
        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;
 | 
			
		||||
        let alwaysZoomOut = this.actor.hover || this._inDrag || !Meta.prefs_get_dynamic_workspaces() || global.screen.n_workspaces > 2;
 | 
			
		||||
 | 
			
		||||
        if (!alwaysZoomOut) {
 | 
			
		||||
            let monitors = Main.layoutManager.monitors;
 | 
			
		||||
@@ -273,20 +285,13 @@ const ThumbnailsSlider = new Lang.Class({
 | 
			
		||||
        return alwaysZoomOut;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOverviewShowing: function() {
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        this.layout.slideX = this.getSlide();
 | 
			
		||||
        this.actor.translation_x = this._getTranslation();
 | 
			
		||||
        this.slideIn();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getNonExpandedWidth: function() {
 | 
			
		||||
        let child = this.actor.get_first_child();
 | 
			
		||||
        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();
 | 
			
		||||
@@ -323,29 +328,22 @@ const DashSlider = new Lang.Class({
 | 
			
		||||
        // available allocation
 | 
			
		||||
        this._dash.actor.x_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));
 | 
			
		||||
        Main.overview.connect('hiding', Lang.bind(this, this.slideOut));
 | 
			
		||||
        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;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOverviewShowing: function() {
 | 
			
		||||
        this.visible = true;
 | 
			
		||||
        this.layout.slideX = this.getSlide();
 | 
			
		||||
        this.actor.translation_x = this._getTranslation();
 | 
			
		||||
        this.slideIn();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onWindowDragBegin: function() {
 | 
			
		||||
        this.fadeHalf();
 | 
			
		||||
    },
 | 
			
		||||
@@ -462,9 +460,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();
 | 
			
		||||
 
 | 
			
		||||
@@ -213,7 +213,7 @@ const AppMenuButton = new Lang.Class({
 | 
			
		||||
        this._label = new TextShadower();
 | 
			
		||||
        this._label.actor.y_align = Clutter.ActorAlign.CENTER;
 | 
			
		||||
        this._hbox.add_actor(this._label.actor);
 | 
			
		||||
        this._arrow = PopupMenu.unicodeArrow(St.Side.BOTTOM);
 | 
			
		||||
        this._arrow = PopupMenu.arrowIcon(St.Side.BOTTOM);
 | 
			
		||||
        this._hbox.add_actor(this._arrow);
 | 
			
		||||
 | 
			
		||||
        this._iconBottomClip = 0;
 | 
			
		||||
@@ -602,14 +602,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) {
 | 
			
		||||
@@ -617,6 +618,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) {
 | 
			
		||||
@@ -628,6 +630,7 @@ const ActivitiesButton = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        Mainloop.source_remove(this._xdndTimeOut);
 | 
			
		||||
        this._xdndTimeOut = 0;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -809,7 +812,11 @@ const AggregateMenu = new Lang.Class({
 | 
			
		||||
        this._indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box' });
 | 
			
		||||
        this.actor.add_child(this._indicators);
 | 
			
		||||
 | 
			
		||||
        this._network = new imports.ui.status.network.NMApplet();
 | 
			
		||||
        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 {
 | 
			
		||||
@@ -822,24 +829,31 @@ const AggregateMenu = new Lang.Class({
 | 
			
		||||
        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._network.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.unicodeArrow(St.Side.BOTTOM));
 | 
			
		||||
        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());
 | 
			
		||||
        this.menu.addMenuItem(this._network.menu);
 | 
			
		||||
        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());
 | 
			
		||||
@@ -983,23 +997,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();
 | 
			
		||||
@@ -1008,7 +1022,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,
 | 
			
		||||
@@ -1020,7 +1034,7 @@ const Panel = new Lang.Class({
 | 
			
		||||
                                     event.get_time(),
 | 
			
		||||
                                     stageX, stageY);
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    toggleAppMenu: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -137,29 +137,30 @@ 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() {
 | 
			
		||||
@@ -172,7 +173,7 @@ const Button = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _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) {
 | 
			
		||||
@@ -180,10 +181,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) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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 Meta = imports.gi.Meta;
 | 
			
		||||
@@ -110,7 +111,7 @@ const PointerWatcher = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onTimeout: function() {
 | 
			
		||||
        this._updatePointer();
 | 
			
		||||
        return true;
 | 
			
		||||
        return GLib.SOURCE_CONTINUE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updatePointer: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -45,28 +45,35 @@ function isPopupMenuItemVisible(child) {
 | 
			
		||||
/**
 | 
			
		||||
 * @side Side to which the arrow points.
 | 
			
		||||
 */
 | 
			
		||||
function unicodeArrow(side) {
 | 
			
		||||
    let arrowChar;
 | 
			
		||||
function arrowIcon(side) {
 | 
			
		||||
    let rotation;
 | 
			
		||||
    switch (side) {
 | 
			
		||||
        case St.Side.TOP:
 | 
			
		||||
            arrowChar = '\u25B4';
 | 
			
		||||
            rotation = 180;
 | 
			
		||||
            break;
 | 
			
		||||
        case St.Side.RIGHT:
 | 
			
		||||
            arrowChar = '\u25B8';
 | 
			
		||||
            rotation = - 90;
 | 
			
		||||
            break;
 | 
			
		||||
        case St.Side.BOTTOM:
 | 
			
		||||
            arrowChar = '\u25BE';
 | 
			
		||||
            rotation = 0;
 | 
			
		||||
            break;
 | 
			
		||||
        case St.Side.LEFT:
 | 
			
		||||
            arrowChar = '\u25C2';
 | 
			
		||||
            rotation = 90;
 | 
			
		||||
            break;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return new St.Label({ text: arrowChar,
 | 
			
		||||
                          style_class: 'unicode-arrow',
 | 
			
		||||
                          accessible_role: Atk.Role.ARROW,
 | 
			
		||||
                          y_expand: true,
 | 
			
		||||
                          y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
    let gicon = new Gio.FileIcon({ file: Gio.File.new_for_path(global.datadir +
 | 
			
		||||
                                             '/theme/menu-arrow-symbolic.svg') });
 | 
			
		||||
 | 
			
		||||
    let arrow = new St.Icon({ style_class: 'popup-menu-arrow',
 | 
			
		||||
                              gicon: gicon,
 | 
			
		||||
                              accessible_role: Atk.Role.ARROW,
 | 
			
		||||
                              y_expand: true,
 | 
			
		||||
                              y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
 | 
			
		||||
    arrow.rotation_angle_z = rotation;
 | 
			
		||||
 | 
			
		||||
    return arrow;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const PopupBaseMenuItem = new Lang.Class({
 | 
			
		||||
@@ -111,6 +118,7 @@ const PopupBaseMenuItem = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn));
 | 
			
		||||
        this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut));
 | 
			
		||||
        this.actor.connect('destroy', Lang.bind(this, this._onDestroy));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getTopMenu: function() {
 | 
			
		||||
@@ -126,7 +134,7 @@ const PopupBaseMenuItem = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onButtonReleaseEvent: function (actor, event) {
 | 
			
		||||
        this.activate(event);
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyPressEvent: function (actor, event) {
 | 
			
		||||
@@ -134,9 +142,9 @@ const PopupBaseMenuItem = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) {
 | 
			
		||||
            this.activate(event);
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyFocusIn: function (actor) {
 | 
			
		||||
@@ -192,6 +200,9 @@ const PopupBaseMenuItem = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this.actor.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDestroy: function() {
 | 
			
		||||
        this.emit('destroy');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -868,14 +879,14 @@ const PopupSubMenu = new Lang.Class({
 | 
			
		||||
        if (animate) {
 | 
			
		||||
            let [minHeight, naturalHeight] = this.actor.get_preferred_height(-1);
 | 
			
		||||
            this.actor.height = 0;
 | 
			
		||||
            this.actor._arrow_rotation = this._arrow.rotation_angle_z;
 | 
			
		||||
            this.actor._arrowRotation = this._arrow.rotation_angle_z;
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { _arrow_rotation: 90,
 | 
			
		||||
                             { _arrowRotation: this.actor._arrowRotation + 90,
 | 
			
		||||
                               height: naturalHeight,
 | 
			
		||||
                               time: 0.25,
 | 
			
		||||
                               onUpdateScope: this,
 | 
			
		||||
                               onUpdate: function() {
 | 
			
		||||
                                   this._arrow.rotation_angle_z = this.actor._arrow_rotation;
 | 
			
		||||
                                   this._arrow.rotation_angle_z = this.actor._arrowRotation;
 | 
			
		||||
                               },
 | 
			
		||||
                               onCompleteScope: this,
 | 
			
		||||
                               onComplete: function() {
 | 
			
		||||
@@ -883,7 +894,7 @@ const PopupSubMenu = new Lang.Class({
 | 
			
		||||
                               }
 | 
			
		||||
                             });
 | 
			
		||||
        } else {
 | 
			
		||||
            this._arrow.rotation_angle_z = 90;
 | 
			
		||||
            this._arrow.rotation_angle_z = this.actor._arrowRotation + 90;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -901,14 +912,14 @@ const PopupSubMenu = new Lang.Class({
 | 
			
		||||
            animate = false;
 | 
			
		||||
 | 
			
		||||
        if (animate) {
 | 
			
		||||
            this.actor._arrow_rotation = this._arrow.rotation_angle_z;
 | 
			
		||||
            this.actor._arrowRotation = this._arrow.rotation_angle_z;
 | 
			
		||||
            Tweener.addTween(this.actor,
 | 
			
		||||
                             { _arrow_rotation: 0,
 | 
			
		||||
                             { _arrowRotation: this.actor._arrowRotation - 90,
 | 
			
		||||
                               height: 0,
 | 
			
		||||
                               time: 0.25,
 | 
			
		||||
                               onUpdateScope: this,
 | 
			
		||||
                               onUpdate: function() {
 | 
			
		||||
                                   this._arrow.rotation_angle_z = this.actor._arrow_rotation;
 | 
			
		||||
                                   this._arrow.rotation_angle_z = this.actor._arrowRotation;
 | 
			
		||||
                               },
 | 
			
		||||
                               onCompleteScope: this,
 | 
			
		||||
                               onComplete: function() {
 | 
			
		||||
@@ -917,7 +928,7 @@ const PopupSubMenu = new Lang.Class({
 | 
			
		||||
                               },
 | 
			
		||||
                             });
 | 
			
		||||
        } else {
 | 
			
		||||
            this._arrow.rotation_angle_z = 0;
 | 
			
		||||
            this._arrow.rotation_angle_z = this.actor._arrowRotation - 90;
 | 
			
		||||
            this.actor.hide();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
@@ -928,10 +939,10 @@ const PopupSubMenu = new Lang.Class({
 | 
			
		||||
        if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) {
 | 
			
		||||
            this.close(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
            this.sourceActor._delegate.setActive(true);
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -989,7 +1000,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
 | 
			
		||||
                                     y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        this.actor.add_child(this.status);
 | 
			
		||||
 | 
			
		||||
        this._triangle = unicodeArrow(St.Side.RIGHT);
 | 
			
		||||
        this._triangle = arrowIcon(St.Side.RIGHT);
 | 
			
		||||
        this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 });
 | 
			
		||||
 | 
			
		||||
        this._triangleBin = new St.Widget({ y_expand: true,
 | 
			
		||||
@@ -1056,10 +1067,10 @@ const PopupSubMenuMenuItem = new Lang.Class({
 | 
			
		||||
        if (symbol == Clutter.KEY_Right) {
 | 
			
		||||
            this._setOpenState(true);
 | 
			
		||||
            this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false);
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        } else if (symbol == Clutter.KEY_Left && this._getOpenState()) {
 | 
			
		||||
            this._setOpenState(false);
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this.parent(actor, event);
 | 
			
		||||
@@ -1071,6 +1082,7 @@ const PopupSubMenuMenuItem = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onButtonReleaseEvent: function(actor) {
 | 
			
		||||
        this._setOpenState(!this._getOpenState());
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -1102,7 +1114,7 @@ const PopupMenuManager = new Lang.Class({
 | 
			
		||||
        if (source) {
 | 
			
		||||
            if (!menu.blockSourceEvents)
 | 
			
		||||
                this._grabHelper.addActor(source);
 | 
			
		||||
            menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
 | 
			
		||||
            menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { return this._onMenuSourceEnter(menu); }));
 | 
			
		||||
            menudata.focusInId = source.connect('key-focus-in', Lang.bind(this, function() { this._onMenuSourceEnter(menu); }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -1114,7 +1126,7 @@ const PopupMenuManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    removeMenu: function(menu) {
 | 
			
		||||
        if (menu == this.activeMenu)
 | 
			
		||||
            this._closeMenu(menu);
 | 
			
		||||
            this._closeMenu(false, menu);
 | 
			
		||||
 | 
			
		||||
        let position = this._findMenu(menu);
 | 
			
		||||
        if (position == -1) // not a menu we manage
 | 
			
		||||
@@ -1164,13 +1176,13 @@ const PopupMenuManager = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _onMenuSourceEnter: function(menu) {
 | 
			
		||||
        if (!this._grabHelper.grabbed)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        if (this._grabHelper.isActorGrabbed(menu.actor))
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        this._changeMenu(menu);
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMenuDestroy: function(menu) {
 | 
			
		||||
 
 | 
			
		||||
@@ -7,58 +7,63 @@ const Lang = imports.lang;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const FileUtils = imports.misc.fileUtils;
 | 
			
		||||
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) {
 | 
			
		||||
function loadRemoteSearchProviders(callback) {
 | 
			
		||||
    let objectPaths = {};
 | 
			
		||||
    let loadedProviders = [];
 | 
			
		||||
 | 
			
		||||
@@ -112,30 +117,25 @@ function loadRemoteSearchProviders(addProviderCallback) {
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    let dataDirs = GLib.get_system_data_dirs();
 | 
			
		||||
    dataDirs.forEach(function(dataDir) {
 | 
			
		||||
        let path = GLib.build_filenamev([dataDir, 'gnome-shell', 'search-providers']);
 | 
			
		||||
        let dir = Gio.File.new_for_path(path);
 | 
			
		||||
        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)))
 | 
			
		||||
                loadRemoteSearchProvider(fileEnum.get_child(info));
 | 
			
		||||
        }
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    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');
 | 
			
		||||
 | 
			
		||||
    loadedProviders = loadedProviders.filter(function(provider) {
 | 
			
		||||
        let appId = provider.appInfo.get_id();
 | 
			
		||||
        let disabled = searchSettings.get_strv('disabled');
 | 
			
		||||
        return disabled.indexOf(appId) == -1;
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    loadedProviders.sort(function(providerA, providerB) {
 | 
			
		||||
        let idxA, idxB;
 | 
			
		||||
        let appIdA, appIdB;
 | 
			
		||||
@@ -166,32 +166,34 @@ function loadRemoteSearchProviders(addProviderCallback) {
 | 
			
		||||
        return (idxA - idxB);
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
    loadedProviders.forEach(addProviderCallback);
 | 
			
		||||
    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']) {
 | 
			
		||||
@@ -203,8 +205,10 @@ 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;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    filterResults: function(results, maxNumber) {
 | 
			
		||||
@@ -217,40 +221,33 @@ const RemoteSearchProvider = new Lang.Class({
 | 
			
		||||
        return regularResults.slice(0, maxNumber).concat(specialResults.slice(0, maxNumber));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getResultsFinished: function(results, error) {
 | 
			
		||||
        if (error)
 | 
			
		||||
    _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;
 | 
			
		||||
        }
 | 
			
		||||
@@ -272,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) {
 | 
			
		||||
@@ -293,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));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -302,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
 | 
			
		||||
 
 | 
			
		||||
@@ -17,7 +17,6 @@ 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;
 | 
			
		||||
@@ -115,7 +114,7 @@ const NotificationsBox = new Lang.Class({
 | 
			
		||||
        this.actor.add(this._musicBin);
 | 
			
		||||
        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);
 | 
			
		||||
        }));
 | 
			
		||||
@@ -130,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);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -198,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' });
 | 
			
		||||
@@ -240,10 +238,6 @@ const NotificationsBox = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sourceAdded: function(tray, source, initial) {
 | 
			
		||||
        // Ignore transient sources
 | 
			
		||||
        if (source.isTransient)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let obj = {
 | 
			
		||||
            visible: source.policy.showInLockScreen,
 | 
			
		||||
            detailed: source.policy.detailsInLockScreen,
 | 
			
		||||
@@ -307,7 +301,7 @@ const NotificationsBox = new Lang.Class({
 | 
			
		||||
                             });
 | 
			
		||||
 | 
			
		||||
            this._updateVisibility();
 | 
			
		||||
            Shell.util_wake_up_screen();
 | 
			
		||||
            this.emit('wake-up-screen');
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -333,7 +327,7 @@ const NotificationsBox = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._updateVisibility();
 | 
			
		||||
        if (obj.sourceBox.visible)
 | 
			
		||||
            Shell.util_wake_up_screen();
 | 
			
		||||
            this.emit('wake-up-screen');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _visibleChanged: function(source, obj) {
 | 
			
		||||
@@ -348,7 +342,7 @@ const NotificationsBox = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._updateVisibility();
 | 
			
		||||
        if (obj.sourceBox.visible)
 | 
			
		||||
            Shell.util_wake_up_screen();
 | 
			
		||||
            this.emit('wake-up-screen');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _detailedChanged: function(source, obj) {
 | 
			
		||||
@@ -386,6 +380,7 @@ const NotificationsBox = new Lang.Class({
 | 
			
		||||
        this._sources.delete(source);
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(NotificationsBox.prototype);
 | 
			
		||||
 | 
			
		||||
const Arrow = new Lang.Class({
 | 
			
		||||
    Name: 'Arrow',
 | 
			
		||||
@@ -419,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() {
 | 
			
		||||
@@ -541,7 +537,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._smartcardManager = SmartcardManager.getSmartcardManager();
 | 
			
		||||
        this._smartcardManager.connect('smartcard-inserted',
 | 
			
		||||
                                       Lang.bind(this, function(token) {
 | 
			
		||||
                                       Lang.bind(this, function(manager, token) {
 | 
			
		||||
                                           if (this._isLocked && token.UsedToLogin)
 | 
			
		||||
                                               this._liftShield(true, 0);
 | 
			
		||||
                                       }));
 | 
			
		||||
@@ -673,11 +669,11 @@ 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;
 | 
			
		||||
 | 
			
		||||
        if (this._isLocked &&
 | 
			
		||||
            this._ensureUnlockDialog(true, true) &&
 | 
			
		||||
@@ -685,12 +681,12 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
            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)
 | 
			
		||||
@@ -705,7 +701,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
            this._liftShield(true, 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _inhibitSuspend: function() {
 | 
			
		||||
@@ -733,7 +729,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
        } else {
 | 
			
		||||
            this._inhibitSuspend();
 | 
			
		||||
 | 
			
		||||
            this._onUserBecameActive();
 | 
			
		||||
            this._wakeUpScreen();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -756,7 +752,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
                             });
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return true;
 | 
			
		||||
        return GLib.SOURCE_CONTINUE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onDragBegin: function() {
 | 
			
		||||
@@ -852,7 +848,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
                                                       Lang.bind(this, function() {
 | 
			
		||||
                                                           this._lockTimeoutId = 0;
 | 
			
		||||
                                                           this.lock(false);
 | 
			
		||||
                                                           return false;
 | 
			
		||||
                                                           return GLib.SOURCE_REMOVE;
 | 
			
		||||
                                                       }));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
@@ -905,17 +901,11 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    showDialog: function() {
 | 
			
		||||
        // Ensure that the stage window is mapped, before taking a grab
 | 
			
		||||
        // otherwise X errors out
 | 
			
		||||
        Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() {
 | 
			
		||||
            if (!this._becomeModal()) {
 | 
			
		||||
                // In the login screen, this is a hard error. Fail-whale
 | 
			
		||||
                log('Could not acquire modal grab for the login screen. Aborting login process.');
 | 
			
		||||
                Meta.quit(Meta.ExitCode.ERROR);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
        }));
 | 
			
		||||
        if (!this._becomeModal()) {
 | 
			
		||||
            // In the login screen, this is a hard error. Fail-whale
 | 
			
		||||
            log('Could not acquire modal grab for the login screen. Aborting login process.');
 | 
			
		||||
            Meta.quit(Meta.ExitCode.ERROR);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actor.show();
 | 
			
		||||
        this._isGreeter = Main.sessionMode.isGreeter;
 | 
			
		||||
@@ -1101,7 +1091,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
                global.stage.disconnect(motionId);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
        }));
 | 
			
		||||
        this._cursorTracker.set_pointer_visible(false);
 | 
			
		||||
 | 
			
		||||
@@ -1114,6 +1104,7 @@ const ScreenShield = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
            Mainloop.timeout_add(1000 * MANUAL_FADE_TIME, Lang.bind(this, function() {
 | 
			
		||||
                this._activateFade(this._shortLightbox, MANUAL_FADE_TIME);
 | 
			
		||||
                return GLib.SOURCE_REMOVE;
 | 
			
		||||
            }));
 | 
			
		||||
        } else {
 | 
			
		||||
            if (params.fadeToBlack)
 | 
			
		||||
@@ -1155,6 +1146,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 });
 | 
			
		||||
@@ -1162,11 +1154,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;
 | 
			
		||||
        }
 | 
			
		||||
 
 | 
			
		||||
@@ -6,30 +6,31 @@ 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',
 | 
			
		||||
@@ -40,13 +41,13 @@ 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._sessionUpdated));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get isRecording() {
 | 
			
		||||
        return this._recorders.size() > 0;
 | 
			
		||||
        return this._recorders.size > 0;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureRecorderForSender: function(sender) {
 | 
			
		||||
@@ -67,8 +68,7 @@ const ScreencastService = new Lang.Class({
 | 
			
		||||
        if (Main.sessionMode.allowScreencast)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        for (let sender in this._recorders.keys())
 | 
			
		||||
            this._recorders.delete(sender);
 | 
			
		||||
        this._recorders.clear();
 | 
			
		||||
        this.emit('updated');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -103,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);
 | 
			
		||||
@@ -122,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);
 | 
			
		||||
@@ -131,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);
 | 
			
		||||
 
 | 
			
		||||
@@ -15,45 +15,47 @@ 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',
 | 
			
		||||
@@ -65,6 +67,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);
 | 
			
		||||
@@ -75,11 +84,31 @@ const ScreenshotService = new Lang.Class({
 | 
			
		||||
        invocation.return_value(retval);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _scaleArea: function(x, y, width, height) {
 | 
			
		||||
        let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
 | 
			
		||||
        x *= scaleFactor;
 | 
			
		||||
        y *= scaleFactor;
 | 
			
		||||
        width *= scaleFactor;
 | 
			
		||||
        height *= scaleFactor;
 | 
			
		||||
        return [x, y, width, height];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _unscaleArea: function(x, y, width, height) {
 | 
			
		||||
        let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor;
 | 
			
		||||
        x /= scaleFactor;
 | 
			
		||||
        y /= scaleFactor;
 | 
			
		||||
        width /= scaleFactor;
 | 
			
		||||
        height /= scaleFactor;
 | 
			
		||||
        return [x, y, width, height];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    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");
 | 
			
		||||
        [x, y, width, height] = this._scaleArea(x, y, width, height);
 | 
			
		||||
        if (!this._checkArea(x, y, width, height)) {
 | 
			
		||||
            invocation.return_error_literal(Gio.IOErrorEnum,
 | 
			
		||||
                                            Gio.IOErrorEnum.CANCELLED,
 | 
			
		||||
                                            "Invalid params");
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        let screenshot = new Shell.Screenshot();
 | 
			
		||||
@@ -110,9 +139,9 @@ const ScreenshotService = new Lang.Class({
 | 
			
		||||
        selectArea.connect('finished', Lang.bind(this,
 | 
			
		||||
            function(selectArea, areaRectangle) {
 | 
			
		||||
                if (areaRectangle) {
 | 
			
		||||
                    let retval = GLib.Variant.new('(iiii)',
 | 
			
		||||
                        [areaRectangle.x, areaRectangle.y,
 | 
			
		||||
                         areaRectangle.width, areaRectangle.height]);
 | 
			
		||||
                    let retRectangle = this._unscaleArea(areaRectangle.x, areaRectangle.y,
 | 
			
		||||
                        areaRectangle.width, areaRectangle.height);
 | 
			
		||||
                    let retval = GLib.Variant.new('(iiii)', retRectangle);
 | 
			
		||||
                    invocation.return_value(retval);
 | 
			
		||||
                } else {
 | 
			
		||||
                    invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED,
 | 
			
		||||
@@ -121,9 +150,18 @@ const ScreenshotService = new Lang.Class({
 | 
			
		||||
            }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    FlashArea: function(x, y, width, height) {
 | 
			
		||||
    FlashAreaAsync: function(params, invocation) {
 | 
			
		||||
        let [x, y, width, height] = params;
 | 
			
		||||
        [x, y, width, height] = this._scaleArea(x, y, width, height);
 | 
			
		||||
        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);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -202,12 +240,12 @@ const SelectArea = new Lang.Class({
 | 
			
		||||
        if (event.get_key_symbol() == Clutter.Escape)
 | 
			
		||||
            this._destroy(null, false);
 | 
			
		||||
 | 
			
		||||
        return;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _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();
 | 
			
		||||
@@ -215,19 +253,19 @@ 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;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _destroy: function(geometry, fade) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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;
 | 
			
		||||
@@ -41,7 +42,7 @@ function sleep(milliseconds) {
 | 
			
		||||
    Mainloop.timeout_add(milliseconds, function() {
 | 
			
		||||
                             if (cb)
 | 
			
		||||
                                 cb();
 | 
			
		||||
                             return false;
 | 
			
		||||
                             return GLib.SOURCE_REMOVE;
 | 
			
		||||
                         });
 | 
			
		||||
 | 
			
		||||
    return function(callback) {
 | 
			
		||||
@@ -69,16 +70,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() {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										738
									
								
								js/ui/search.js
									
									
									
									
									
								
							
							
						
						
									
										738
									
								
								js/ui/search.js
									
									
									
									
									
								
							@@ -1,105 +1,729 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
const AppDisplay = imports.ui.appDisplay;
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const IconGrid = imports.ui.iconGrid;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Overview = imports.ui.overview;
 | 
			
		||||
const RemoteSearch = imports.ui.remoteSearch;
 | 
			
		||||
const Separator = imports.ui.separator;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const SEARCH_PROVIDERS_SCHEMA = 'org.gnome.desktop.search-providers';
 | 
			
		||||
 | 
			
		||||
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
 | 
			
		||||
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
 | 
			
		||||
 | 
			
		||||
const SearchSystem = new Lang.Class({
 | 
			
		||||
    Name: 'SearchSystem',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._providers = [];
 | 
			
		||||
        this._remoteProviders = [];
 | 
			
		||||
        this.reset();
 | 
			
		||||
 | 
			
		||||
        this._registerProvider(new AppDisplay.AppSearchProvider());
 | 
			
		||||
 | 
			
		||||
        this._searchSettings = new Gio.Settings({ schema: SEARCH_PROVIDERS_SCHEMA });
 | 
			
		||||
        this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders));
 | 
			
		||||
        this._searchSettings.connect('changed::disable-external', Lang.bind(this, this._reloadRemoteProviders));
 | 
			
		||||
        this._searchSettings.connect('changed::sort-order', Lang.bind(this, this._reloadRemoteProviders));
 | 
			
		||||
 | 
			
		||||
        this._reloadRemoteProviders();
 | 
			
		||||
 | 
			
		||||
        this._cancellable = new Gio.Cancellable();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    registerProvider: function (provider) {
 | 
			
		||||
        provider.searchSystem = this;
 | 
			
		||||
    addProvider: function(provider) {
 | 
			
		||||
        this._providers.push(provider);
 | 
			
		||||
 | 
			
		||||
        if (provider.isRemoteProvider)
 | 
			
		||||
            this._remoteProviders.push(provider);
 | 
			
		||||
        this.emit('providers-changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    unregisterProvider: function (provider) {
 | 
			
		||||
    _reloadRemoteProviders: function() {
 | 
			
		||||
        let remoteProviders = this._providers.filter(function(provider) {
 | 
			
		||||
            return provider.isRemoteProvider;
 | 
			
		||||
        });
 | 
			
		||||
        remoteProviders.forEach(Lang.bind(this, function(provider) {
 | 
			
		||||
            this._unregisterProvider(provider);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, function(providers) {
 | 
			
		||||
            providers.forEach(Lang.bind(this, this._registerProvider));
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this.emit('providers-changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _registerProvider: function (provider) {
 | 
			
		||||
        this._providers.push(provider);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _unregisterProvider: function (provider) {
 | 
			
		||||
        let index = this._providers.indexOf(provider);
 | 
			
		||||
        if (index == -1)
 | 
			
		||||
            return;
 | 
			
		||||
        provider.searchSystem = null;
 | 
			
		||||
        this._providers.splice(index, 1);
 | 
			
		||||
 | 
			
		||||
        let remoteIndex = this._remoteProviders.indexOf(provider);
 | 
			
		||||
        if (remoteIndex != -1)
 | 
			
		||||
            this._remoteProviders.splice(remoteIndex, 1);
 | 
			
		||||
        if (provider.display)
 | 
			
		||||
            provider.display.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getProviders: function() {
 | 
			
		||||
        return this._providers;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getRemoteProviders: function() {
 | 
			
		||||
        return this._remoteProviders;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getTerms: function() {
 | 
			
		||||
        return this._previousTerms;
 | 
			
		||||
        return this._terms;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    reset: function() {
 | 
			
		||||
        this._previousTerms = [];
 | 
			
		||||
        this._previousResults = [];
 | 
			
		||||
        this._terms = [];
 | 
			
		||||
        this._results = {};
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setResults: function(provider, results) {
 | 
			
		||||
        let i = this._providers.indexOf(provider);
 | 
			
		||||
        if (i == -1)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        this._previousResults[i] = [provider, results];
 | 
			
		||||
        this.emit('search-updated', this._previousResults[i]);
 | 
			
		||||
    _gotResults: function(results, provider) {
 | 
			
		||||
        this._results[provider.id] = results;
 | 
			
		||||
        this.emit('search-updated', provider, results);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateSearchResults: function(terms) {
 | 
			
		||||
    setTerms: function(terms) {
 | 
			
		||||
        this._cancellable.cancel();
 | 
			
		||||
        this._cancellable.reset();
 | 
			
		||||
 | 
			
		||||
        let previousResults = this._results;
 | 
			
		||||
        let previousTerms = this._terms;
 | 
			
		||||
        this.reset();
 | 
			
		||||
 | 
			
		||||
        if (!terms)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let searchString = terms.join(' ');
 | 
			
		||||
        let previousSearchString = this._previousTerms.join(' ');
 | 
			
		||||
        let previousSearchString = previousTerms.join(' ');
 | 
			
		||||
        if (searchString == previousSearchString)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let isSubSearch = false;
 | 
			
		||||
        if (this._previousTerms.length > 0)
 | 
			
		||||
        if (previousTerms.length > 0)
 | 
			
		||||
            isSubSearch = searchString.indexOf(previousSearchString) == 0;
 | 
			
		||||
 | 
			
		||||
        let previousResultsArr = this._previousResults;
 | 
			
		||||
        this._terms = terms;
 | 
			
		||||
 | 
			
		||||
        let results = [];
 | 
			
		||||
        this._previousTerms = terms;
 | 
			
		||||
        this._previousResults = results;
 | 
			
		||||
 | 
			
		||||
        if (isSubSearch) {
 | 
			
		||||
            for (let i = 0; i < this._providers.length; i++) {
 | 
			
		||||
                let [provider, previousResults] = previousResultsArr[i];
 | 
			
		||||
                try {
 | 
			
		||||
                    results.push([provider, []]);
 | 
			
		||||
                    provider.getSubsearchResultSet(previousResults, terms);
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            for (let i = 0; i < this._providers.length; i++) {
 | 
			
		||||
                let provider = this._providers[i];
 | 
			
		||||
                try {
 | 
			
		||||
                    results.push([provider, []]);
 | 
			
		||||
                    provider.getInitialResultSet(terms);
 | 
			
		||||
                } catch (error) {
 | 
			
		||||
                    log('A ' + error.name + ' has occured in ' + provider.id + ': ' + error.message);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        this._providers.forEach(Lang.bind(this, function(provider) {
 | 
			
		||||
            let previousProviderResults = previousResults[provider.id];
 | 
			
		||||
            if (isSubSearch && previousProviderResults)
 | 
			
		||||
                provider.getSubsearchResultSet(previousProviderResults, terms, Lang.bind(this, this._gotResults, provider), this._cancellable);
 | 
			
		||||
            else
 | 
			
		||||
                provider.getInitialResultSet(terms, Lang.bind(this, this._gotResults, provider), this._cancellable);
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(SearchSystem.prototype);
 | 
			
		||||
 | 
			
		||||
const MaxWidthBin = new Lang.Class({
 | 
			
		||||
    Name: 'MaxWidthBin',
 | 
			
		||||
    Extends: St.Bin,
 | 
			
		||||
 | 
			
		||||
    vfunc_allocate: function(box, flags) {
 | 
			
		||||
        let themeNode = this.get_theme_node();
 | 
			
		||||
        let maxWidth = themeNode.get_max_width();
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let adjustedBox = box;
 | 
			
		||||
 | 
			
		||||
        if (availWidth > maxWidth) {
 | 
			
		||||
            let excessWidth = availWidth - maxWidth;
 | 
			
		||||
            adjustedBox.x1 += Math.floor(excessWidth / 2);
 | 
			
		||||
            adjustedBox.x2 -= Math.floor(excessWidth / 2);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.parent(adjustedBox, flags);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const SearchResult = new Lang.Class({
 | 
			
		||||
    Name: 'SearchResult',
 | 
			
		||||
 | 
			
		||||
    _init: function(provider, metaInfo) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
        this.metaInfo = metaInfo;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Button({ reactive: true,
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     track_hover: true,
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     y_fill: true });
 | 
			
		||||
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this.activate));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activate: function() {
 | 
			
		||||
        this.emit('activate', this.metaInfo.id);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setSelected: function(selected) {
 | 
			
		||||
        if (selected)
 | 
			
		||||
            this.actor.add_style_pseudo_class('selected');
 | 
			
		||||
        else
 | 
			
		||||
            this.actor.remove_style_pseudo_class('selected');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(SearchResult.prototype);
 | 
			
		||||
 | 
			
		||||
const ListSearchResult = new Lang.Class({
 | 
			
		||||
    Name: 'ListSearchResult',
 | 
			
		||||
    Extends: SearchResult,
 | 
			
		||||
 | 
			
		||||
    ICON_SIZE: 64,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider, metaInfo) {
 | 
			
		||||
        this.parent(provider, metaInfo);
 | 
			
		||||
 | 
			
		||||
        this.actor.style_class = 'list-search-result';
 | 
			
		||||
        this.actor.x_fill = true;
 | 
			
		||||
 | 
			
		||||
        let content = new St.BoxLayout({ style_class: 'list-search-result-content',
 | 
			
		||||
                                         vertical: false });
 | 
			
		||||
        this.actor.set_child(content);
 | 
			
		||||
 | 
			
		||||
        // An icon for, or thumbnail of, content
 | 
			
		||||
        let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
 | 
			
		||||
        if (icon) {
 | 
			
		||||
            content.add(icon);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let details = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        content.add(details, { x_fill: true,
 | 
			
		||||
                               y_fill: false,
 | 
			
		||||
                               x_align: St.Align.START,
 | 
			
		||||
                               y_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        let title = new St.Label({ style_class: 'list-search-result-title',
 | 
			
		||||
                                   text: this.metaInfo['name'] })
 | 
			
		||||
        details.add(title, { x_fill: false,
 | 
			
		||||
                             y_fill: false,
 | 
			
		||||
                             x_align: St.Align.START,
 | 
			
		||||
                             y_align: St.Align.START });
 | 
			
		||||
        this.actor.label_actor = title;
 | 
			
		||||
 | 
			
		||||
        if (this.metaInfo['description']) {
 | 
			
		||||
            let description = new St.Label({ style_class: 'list-search-result-description' });
 | 
			
		||||
            description.clutter_text.set_markup(this.metaInfo['description']);
 | 
			
		||||
            details.add(description, { x_fill: false,
 | 
			
		||||
                                       y_fill: false,
 | 
			
		||||
                                       x_align: St.Align.START,
 | 
			
		||||
                                       y_align: St.Align.END });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const GridSearchResult = new Lang.Class({
 | 
			
		||||
    Name: 'GridSearchResult',
 | 
			
		||||
    Extends: SearchResult,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider, metaInfo) {
 | 
			
		||||
        this.parent(provider, metaInfo);
 | 
			
		||||
 | 
			
		||||
        this.actor.style_class = 'grid-search-result';
 | 
			
		||||
 | 
			
		||||
        let content = provider.createResultObject(metaInfo);
 | 
			
		||||
        let dragSource = null;
 | 
			
		||||
 | 
			
		||||
        if (content == null) {
 | 
			
		||||
            let actor = new St.Bin();
 | 
			
		||||
            let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
 | 
			
		||||
                                             { createIcon: this.metaInfo['createIcon'] });
 | 
			
		||||
            actor.set_child(icon.actor);
 | 
			
		||||
            actor.label_actor = icon.label;
 | 
			
		||||
            dragSource = icon.icon;
 | 
			
		||||
            content = { actor: actor, icon: icon };
 | 
			
		||||
        } else {
 | 
			
		||||
            if (content._delegate && content._delegate.getDragActorSource)
 | 
			
		||||
                dragSource = content._delegate.getDragActorSource();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actor.set_child(content.actor);
 | 
			
		||||
        this.actor.label_actor = content.actor.label_actor;
 | 
			
		||||
        this.icon = content.icon;
 | 
			
		||||
 | 
			
		||||
        let draggable = DND.makeDraggable(this.actor);
 | 
			
		||||
        draggable.connect('drag-begin',
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.beginItemDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
        draggable.connect('drag-cancelled',
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.cancelledItemDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
        draggable.connect('drag-end',
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.endItemDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
 | 
			
		||||
        if (!dragSource)
 | 
			
		||||
            // not exactly right, but alignment problems are hard to notice
 | 
			
		||||
            dragSource = content;
 | 
			
		||||
        this._dragActorSource = dragSource;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getDragActorSource: function() {
 | 
			
		||||
        return this._dragActorSource;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getDragActor: function() {
 | 
			
		||||
        return this.metaInfo['createIcon'](Main.overview.dashIconSize);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    shellWorkspaceLaunch: function(params) {
 | 
			
		||||
        if (this.provider.dragActivateResult)
 | 
			
		||||
            this.provider.dragActivateResult(this.metaInfo.id, params);
 | 
			
		||||
        else
 | 
			
		||||
            this.provider.activateResult(this.metaInfo.id, this.terms);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const SearchResultsBase = new Lang.Class({
 | 
			
		||||
    Name: 'SearchResultsBase',
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
 | 
			
		||||
        this._terms = [];
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'search-section',
 | 
			
		||||
                                        vertical: true });
 | 
			
		||||
 | 
			
		||||
        this._resultDisplayBin = new St.Bin({ x_fill: true,
 | 
			
		||||
                                              y_fill: true });
 | 
			
		||||
        this.actor.add(this._resultDisplayBin, { expand: true });
 | 
			
		||||
 | 
			
		||||
        let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
 | 
			
		||||
        this.actor.add(separator.actor);
 | 
			
		||||
 | 
			
		||||
        this._resultDisplays = {};
 | 
			
		||||
 | 
			
		||||
        this._cancellable = new Gio.Cancellable();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this.actor.destroy();
 | 
			
		||||
        this._terms = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearResultDisplay: function() {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    clear: function() {
 | 
			
		||||
        for (let resultId in this._resultDisplays)
 | 
			
		||||
            this._resultDisplays[resultId].actor.destroy();
 | 
			
		||||
        this._resultDisplays = {};
 | 
			
		||||
        this._clearResultDisplay();
 | 
			
		||||
        this.actor.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _keyFocusIn: function(actor) {
 | 
			
		||||
        this.emit('key-focus-in', actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _activateResult: function(result, id) {
 | 
			
		||||
        this.provider.activateResult(id, this._terms);
 | 
			
		||||
        Main.overview.toggle();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setMoreIconVisible: function(visible) {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureResultActors: function(results, callback) {
 | 
			
		||||
        let metasNeeded = results.filter(Lang.bind(this, function(resultId) {
 | 
			
		||||
            return this._resultDisplays[resultId] === undefined;
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        if (metasNeeded.length === 0) {
 | 
			
		||||
            callback(true);
 | 
			
		||||
        } else {
 | 
			
		||||
            this._cancellable.cancel();
 | 
			
		||||
            this._cancellable.reset();
 | 
			
		||||
 | 
			
		||||
            this.provider.getResultMetas(metasNeeded, Lang.bind(this, function(metas) {
 | 
			
		||||
                if (metas.length == 0) {
 | 
			
		||||
                    callback(false);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                if (metas.length != metasNeeded.length) {
 | 
			
		||||
                    log('Wrong number of result metas returned by search provider');
 | 
			
		||||
                    callback(false);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
                metasNeeded.forEach(Lang.bind(this, function(resultId, i) {
 | 
			
		||||
                    let meta = metas[i];
 | 
			
		||||
                    let display = this._createResultDisplay(meta);
 | 
			
		||||
                    display.connect('activate', Lang.bind(this, this._activateResult));
 | 
			
		||||
                    display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
 | 
			
		||||
                    this._resultDisplays[resultId] = display;
 | 
			
		||||
                }));
 | 
			
		||||
                callback(true);
 | 
			
		||||
            }), this._cancellable);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateSearch: function(providerResults, terms, callback) {
 | 
			
		||||
        this._terms = terms;
 | 
			
		||||
 | 
			
		||||
        if (providerResults.length == 0) {
 | 
			
		||||
            this._clearResultDisplay();
 | 
			
		||||
            this.actor.hide();
 | 
			
		||||
            callback();
 | 
			
		||||
        } else {
 | 
			
		||||
            let maxResults = this._getMaxDisplayedResults();
 | 
			
		||||
            let results = this.provider.filterResults(providerResults, maxResults);
 | 
			
		||||
            let hasMoreResults = results.length < providerResults.length;
 | 
			
		||||
 | 
			
		||||
            this._ensureResultActors(results, Lang.bind(this, function(successful) {
 | 
			
		||||
                this._clearResultDisplay();
 | 
			
		||||
                if (!successful)
 | 
			
		||||
                    return;
 | 
			
		||||
 | 
			
		||||
                // To avoid CSS transitions causing flickering when
 | 
			
		||||
                // the first search result stays the same, we hide the
 | 
			
		||||
                // content while filling in the results.
 | 
			
		||||
                this.actor.hide();
 | 
			
		||||
                this._clearResultDisplay();
 | 
			
		||||
                results.forEach(Lang.bind(this, function(resultId) {
 | 
			
		||||
                    this._addItem(this._resultDisplays[resultId]);
 | 
			
		||||
                }));
 | 
			
		||||
                this._setMoreIconVisible(hasMoreResults && this.provider.canLaunchSearch);
 | 
			
		||||
                this.actor.show();
 | 
			
		||||
                callback();
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ListSearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'ListSearchResults',
 | 
			
		||||
    Extends: SearchResultsBase,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.parent(provider);
 | 
			
		||||
 | 
			
		||||
        this._container = new St.BoxLayout({ style_class: 'search-section-content' });
 | 
			
		||||
        this.providerIcon = new ProviderIcon(provider);
 | 
			
		||||
        this.providerIcon.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
 | 
			
		||||
        this.providerIcon.connect('clicked', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                provider.launchSearch(this._terms);
 | 
			
		||||
                Main.overview.toggle();
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._container.add(this.providerIcon, { x_fill: false,
 | 
			
		||||
                                                 y_fill: false,
 | 
			
		||||
                                                 x_align: St.Align.START,
 | 
			
		||||
                                                 y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._content = new St.BoxLayout({ style_class: 'list-search-results',
 | 
			
		||||
                                           vertical: true });
 | 
			
		||||
        this._container.add(this._content, { expand: true });
 | 
			
		||||
 | 
			
		||||
        this._resultDisplayBin.set_child(this._container);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setMoreIconVisible: function(visible) {
 | 
			
		||||
        this.providerIcon.moreIcon.visible = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getMaxDisplayedResults: function() {
 | 
			
		||||
        return MAX_LIST_SEARCH_RESULTS_ROWS;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearResultDisplay: function () {
 | 
			
		||||
        this._content.remove_all_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createResultDisplay: function(meta) {
 | 
			
		||||
        return new ListSearchResult(this.provider, meta);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addItem: function(display) {
 | 
			
		||||
        this._content.add_actor(display.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getFirstResult: function() {
 | 
			
		||||
        if (this._content.get_n_children() > 0)
 | 
			
		||||
            return this._content.get_child_at_index(0)._delegate;
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ListSearchResults.prototype);
 | 
			
		||||
 | 
			
		||||
const GridSearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'GridSearchResults',
 | 
			
		||||
    Extends: SearchResultsBase,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.parent(provider);
 | 
			
		||||
 | 
			
		||||
        this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
 | 
			
		||||
                                             xAlign: St.Align.START });
 | 
			
		||||
        this._bin = new St.Bin({ x_align: St.Align.MIDDLE });
 | 
			
		||||
        this._bin.set_child(this._grid.actor);
 | 
			
		||||
 | 
			
		||||
        this._resultDisplayBin.set_child(this._bin);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getMaxDisplayedResults: function() {
 | 
			
		||||
        return this._grid.columnsForWidth(this._bin.width) * this._grid.getRowLimit();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _renderResults: function(metas) {
 | 
			
		||||
        for (let i = 0; i < metas.length; i++) {
 | 
			
		||||
            let display = new GridSearchResult(this.provider, metas[i]);
 | 
			
		||||
            display.connect('activate', Lang.bind(this, this._activateResult));
 | 
			
		||||
            display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
 | 
			
		||||
            this._grid.addItem(display);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearResultDisplay: function () {
 | 
			
		||||
        this._grid.removeAll();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createResultDisplay: function(meta) {
 | 
			
		||||
        return new GridSearchResult(this.provider, meta);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addItem: function(display) {
 | 
			
		||||
        this._grid.addItem(display);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getFirstResult: function() {
 | 
			
		||||
        if (this._grid.visibleItemsCount() > 0)
 | 
			
		||||
            return this._grid.getItemAtIndex(0)._delegate;
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(GridSearchResults.prototype);
 | 
			
		||||
 | 
			
		||||
const SearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'SearchResults',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.actor = new St.BoxLayout({ name: 'searchResults',
 | 
			
		||||
                                        vertical: true });
 | 
			
		||||
 | 
			
		||||
        this._content = new St.BoxLayout({ name: 'searchResultsContent',
 | 
			
		||||
                                           vertical: true });
 | 
			
		||||
        this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
 | 
			
		||||
                                             x_fill: true,
 | 
			
		||||
                                             y_fill: true,
 | 
			
		||||
                                             child: this._content });
 | 
			
		||||
 | 
			
		||||
        let scrollChild = new St.BoxLayout();
 | 
			
		||||
        scrollChild.add(this._contentBin, { expand: true });
 | 
			
		||||
 | 
			
		||||
        this._scrollView = new St.ScrollView({ x_fill: true,
 | 
			
		||||
                                               y_fill: false,
 | 
			
		||||
                                               overlay_scrollbars: true,
 | 
			
		||||
                                               style_class: 'search-display vfade' });
 | 
			
		||||
        this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
        this._scrollView.add_actor(scrollChild);
 | 
			
		||||
        let action = new Clutter.PanAction({ interpolate: true });
 | 
			
		||||
        action.connect('pan', Lang.bind(this, this._onPan));
 | 
			
		||||
        this._scrollView.add_action(action);
 | 
			
		||||
 | 
			
		||||
        this.actor.add(this._scrollView, { x_fill: true,
 | 
			
		||||
                                           y_fill: true,
 | 
			
		||||
                                           expand: true,
 | 
			
		||||
                                           x_align: St.Align.START,
 | 
			
		||||
                                           y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._statusText = new St.Label({ style_class: 'search-statustext' });
 | 
			
		||||
        this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
 | 
			
		||||
                                       y_align: St.Align.MIDDLE });
 | 
			
		||||
        this._content.add(this._statusBin, { expand: true });
 | 
			
		||||
        this._statusBin.add_actor(this._statusText);
 | 
			
		||||
 | 
			
		||||
        this._highlightDefault = false;
 | 
			
		||||
        this._defaultResult = null;
 | 
			
		||||
 | 
			
		||||
        this._searchSystem = new SearchSystem();
 | 
			
		||||
        this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
 | 
			
		||||
        this._searchSystem.connect('providers-changed', Lang.bind(this, this._updateProviderDisplays));
 | 
			
		||||
        this._updateProviderDisplays();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPan: function(action) {
 | 
			
		||||
        let [dist, dx, dy] = action.get_motion_delta(0);
 | 
			
		||||
        let adjustment = this._scrollView.vscroll.adjustment;
 | 
			
		||||
        adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _keyFocusIn: function(provider, actor) {
 | 
			
		||||
        Util.ensureActorVisibleInScrollView(this._scrollView, actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureProviderDisplay: function(provider) {
 | 
			
		||||
        if (provider.display)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        let providerDisplay;
 | 
			
		||||
        if (provider.appInfo)
 | 
			
		||||
            providerDisplay = new ListSearchResults(provider);
 | 
			
		||||
        else
 | 
			
		||||
            providerDisplay = new GridSearchResults(provider);
 | 
			
		||||
 | 
			
		||||
        providerDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
 | 
			
		||||
        this._content.add(providerDisplay.actor);
 | 
			
		||||
        provider.display = providerDisplay;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateProviderDisplays: function() {
 | 
			
		||||
        this._searchSystem.getProviders().forEach(Lang.bind(this, this._ensureProviderDisplay));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearDisplay: function() {
 | 
			
		||||
        this._searchSystem.getProviders().forEach(function(provider) {
 | 
			
		||||
            provider.display.clear();
 | 
			
		||||
        });
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    reset: function() {
 | 
			
		||||
        this._searchSystem.reset();
 | 
			
		||||
        this._statusBin.hide();
 | 
			
		||||
        this._clearDisplay();
 | 
			
		||||
        this._defaultResult = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    startingSearch: function() {
 | 
			
		||||
        this.reset();
 | 
			
		||||
        this._statusText.set_text(_("Searching…"));
 | 
			
		||||
        this._statusBin.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setTerms: function(terms) {
 | 
			
		||||
        this._searchSystem.setTerms(terms);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _maybeSetInitialSelection: function() {
 | 
			
		||||
        let newDefaultResult = null;
 | 
			
		||||
 | 
			
		||||
        let providers = this._searchSystem.getProviders();
 | 
			
		||||
        for (let i = 0; i < providers.length; i++) {
 | 
			
		||||
            let provider = providers[i];
 | 
			
		||||
            let display = provider.display;
 | 
			
		||||
 | 
			
		||||
            if (!display.actor.visible)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            let firstResult = display.getFirstResult();
 | 
			
		||||
            if (firstResult) {
 | 
			
		||||
                newDefaultResult = firstResult;
 | 
			
		||||
                break; // select this one!
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (newDefaultResult != this._defaultResult) {
 | 
			
		||||
            if (this._defaultResult)
 | 
			
		||||
                this._defaultResult.setSelected(false);
 | 
			
		||||
            if (newDefaultResult) {
 | 
			
		||||
                newDefaultResult.setSelected(this._highlightDefault);
 | 
			
		||||
                if (this._highlightDefault)
 | 
			
		||||
                    Util.ensureActorVisibleInScrollView(this._scrollView, newDefaultResult.actor);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            this._defaultResult = newDefaultResult;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateStatusText: function () {
 | 
			
		||||
        let haveResults = this._searchSystem.getProviders().some(function(provider) {
 | 
			
		||||
            let display = provider.display;
 | 
			
		||||
            return (display.getFirstResult() != null);
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
        if (!haveResults) {
 | 
			
		||||
            this._statusText.set_text(_("No results."));
 | 
			
		||||
            this._statusBin.show();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._statusBin.hide();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateResults: function(searchSystem, provider, results) {
 | 
			
		||||
        let terms = searchSystem.getTerms();
 | 
			
		||||
        let display = provider.display;
 | 
			
		||||
 | 
			
		||||
        display.updateSearch(results, terms, Lang.bind(this, function() {
 | 
			
		||||
            this._maybeSetInitialSelection();
 | 
			
		||||
            this._updateStatusText();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateDefault: function() {
 | 
			
		||||
        if (this._defaultResult)
 | 
			
		||||
            this._defaultResult.activate();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    highlightDefault: function(highlight) {
 | 
			
		||||
        this._highlightDefault = highlight;
 | 
			
		||||
        if (this._defaultResult) {
 | 
			
		||||
            this._defaultResult.setSelected(highlight);
 | 
			
		||||
            if (highlight)
 | 
			
		||||
                Util.ensureActorVisibleInScrollView(this._scrollView, this._defaultResult.actor);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    navigateFocus: function(direction) {
 | 
			
		||||
        let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
 | 
			
		||||
        if (direction == Gtk.DirectionType.TAB_BACKWARD ||
 | 
			
		||||
            direction == (rtl ? Gtk.DirectionType.RIGHT
 | 
			
		||||
                              : Gtk.DirectionType.LEFT) ||
 | 
			
		||||
            direction == Gtk.DirectionType.UP) {
 | 
			
		||||
            this.actor.navigate_focus(null, direction, false);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let from = this._defaultResult ? this._defaultResult.actor : null;
 | 
			
		||||
        this.actor.navigate_focus(from, direction, false);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ProviderIcon = new Lang.Class({
 | 
			
		||||
    Name: 'ProviderIcon',
 | 
			
		||||
    Extends: St.Button,
 | 
			
		||||
 | 
			
		||||
    PROVIDER_ICON_SIZE: 48,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
        this.parent({ style_class: 'search-provider-icon',
 | 
			
		||||
                      reactive: true,
 | 
			
		||||
                      can_focus: true,
 | 
			
		||||
                      accessible_name: provider.appInfo.get_name(),
 | 
			
		||||
                      track_hover: true });
 | 
			
		||||
 | 
			
		||||
        this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
 | 
			
		||||
        this.set_child(this._content);
 | 
			
		||||
 | 
			
		||||
        let rtl = (this.get_text_direction() == Clutter.TextDirection.RTL);
 | 
			
		||||
 | 
			
		||||
        this.moreIcon = new St.Widget({ style_class: 'search-provider-icon-more',
 | 
			
		||||
                                        visible: false,
 | 
			
		||||
                                        x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
 | 
			
		||||
                                        y_align: Clutter.ActorAlign.END,
 | 
			
		||||
                                        x_expand: true,
 | 
			
		||||
                                        y_expand: true });
 | 
			
		||||
 | 
			
		||||
        let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE,
 | 
			
		||||
                                 gicon: provider.appInfo.get_icon() });
 | 
			
		||||
        this._content.add_actor(icon);
 | 
			
		||||
        this._content.add_actor(this.moreIcon);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -1,567 +0,0 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Gtk = imports.gi.Gtk;
 | 
			
		||||
const Meta = imports.gi.Meta;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
const DND = imports.ui.dnd;
 | 
			
		||||
const IconGrid = imports.ui.iconGrid;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Overview = imports.ui.overview;
 | 
			
		||||
const Separator = imports.ui.separator;
 | 
			
		||||
const Search = imports.ui.search;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const MAX_LIST_SEARCH_RESULTS_ROWS = 3;
 | 
			
		||||
const MAX_GRID_SEARCH_RESULTS_ROWS = 1;
 | 
			
		||||
 | 
			
		||||
const MaxWidthBin = new Lang.Class({
 | 
			
		||||
    Name: 'MaxWidthBin',
 | 
			
		||||
    Extends: St.Bin,
 | 
			
		||||
 | 
			
		||||
    vfunc_allocate: function(box, flags) {
 | 
			
		||||
        let themeNode = this.get_theme_node();
 | 
			
		||||
        let maxWidth = themeNode.get_max_width();
 | 
			
		||||
        let availWidth = box.x2 - box.x1;
 | 
			
		||||
        let adjustedBox = box;
 | 
			
		||||
 | 
			
		||||
        if (availWidth > maxWidth) {
 | 
			
		||||
            let excessWidth = availWidth - maxWidth;
 | 
			
		||||
            adjustedBox.x1 += Math.floor(excessWidth / 2);
 | 
			
		||||
            adjustedBox.x2 -= Math.floor(excessWidth / 2);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.parent(adjustedBox, flags);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const SearchResult = new Lang.Class({
 | 
			
		||||
    Name: 'SearchResult',
 | 
			
		||||
 | 
			
		||||
    _init: function(provider, metaInfo, terms) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
        this.metaInfo = metaInfo;
 | 
			
		||||
        this.terms = terms;
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Button({ reactive: true,
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     track_hover: true,
 | 
			
		||||
                                     x_align: St.Align.START,
 | 
			
		||||
                                     y_fill: true });
 | 
			
		||||
 | 
			
		||||
        this.actor._delegate = this;
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, this.activate));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activate: function() {
 | 
			
		||||
        this.provider.activateResult(this.metaInfo.id, this.terms);
 | 
			
		||||
        Main.overview.toggle();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setSelected: function(selected) {
 | 
			
		||||
        if (selected)
 | 
			
		||||
            this.actor.add_style_pseudo_class('selected');
 | 
			
		||||
        else
 | 
			
		||||
            this.actor.remove_style_pseudo_class('selected');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ListSearchResult = new Lang.Class({
 | 
			
		||||
    Name: 'ListSearchResult',
 | 
			
		||||
    Extends: SearchResult,
 | 
			
		||||
 | 
			
		||||
    ICON_SIZE: 64,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider, metaInfo, terms) {
 | 
			
		||||
        this.parent(provider, metaInfo, terms);
 | 
			
		||||
 | 
			
		||||
        this.actor.style_class = 'list-search-result';
 | 
			
		||||
        this.actor.x_fill = true;
 | 
			
		||||
 | 
			
		||||
        let content = new St.BoxLayout({ style_class: 'list-search-result-content',
 | 
			
		||||
                                         vertical: false });
 | 
			
		||||
        this.actor.set_child(content);
 | 
			
		||||
 | 
			
		||||
        // An icon for, or thumbnail of, content
 | 
			
		||||
        let icon = this.metaInfo['createIcon'](this.ICON_SIZE);
 | 
			
		||||
        if (icon) {
 | 
			
		||||
            content.add(icon);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let details = new St.BoxLayout({ vertical: true });
 | 
			
		||||
        content.add(details, { x_fill: true,
 | 
			
		||||
                               y_fill: false,
 | 
			
		||||
                               x_align: St.Align.START,
 | 
			
		||||
                               y_align: St.Align.MIDDLE });
 | 
			
		||||
 | 
			
		||||
        let title = new St.Label({ style_class: 'list-search-result-title',
 | 
			
		||||
                                   text: this.metaInfo['name'] })
 | 
			
		||||
        details.add(title, { x_fill: false,
 | 
			
		||||
                             y_fill: false,
 | 
			
		||||
                             x_align: St.Align.START,
 | 
			
		||||
                             y_align: St.Align.START });
 | 
			
		||||
        this.actor.label_actor = title;
 | 
			
		||||
 | 
			
		||||
        if (this.metaInfo['description']) {
 | 
			
		||||
            let description = new St.Label({ style_class: 'list-search-result-description' });
 | 
			
		||||
            description.clutter_text.set_markup(this.metaInfo['description']);
 | 
			
		||||
            details.add(description, { x_fill: false,
 | 
			
		||||
                                       y_fill: false,
 | 
			
		||||
                                       x_align: St.Align.START,
 | 
			
		||||
                                       y_align: St.Align.END });
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const GridSearchResult = new Lang.Class({
 | 
			
		||||
    Name: 'GridSearchResult',
 | 
			
		||||
    Extends: SearchResult,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider, metaInfo, terms) {
 | 
			
		||||
        this.parent(provider, metaInfo, terms);
 | 
			
		||||
 | 
			
		||||
        this.actor.style_class = 'grid-search-result';
 | 
			
		||||
 | 
			
		||||
        let content = provider.createResultObject(metaInfo, terms);
 | 
			
		||||
        let dragSource = null;
 | 
			
		||||
 | 
			
		||||
        if (content == null) {
 | 
			
		||||
            let actor = new St.Bin();
 | 
			
		||||
            let icon = new IconGrid.BaseIcon(this.metaInfo['name'],
 | 
			
		||||
                                             { createIcon: this.metaInfo['createIcon'] });
 | 
			
		||||
            actor.set_child(icon.actor);
 | 
			
		||||
            actor.label_actor = icon.label;
 | 
			
		||||
            dragSource = icon.icon;
 | 
			
		||||
            content = { actor: actor, icon: icon };
 | 
			
		||||
        } else {
 | 
			
		||||
            if (content._delegate && content._delegate.getDragActorSource)
 | 
			
		||||
                dragSource = content._delegate.getDragActorSource();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.actor.set_child(content.actor);
 | 
			
		||||
        this.actor.label_actor = content.actor.label_actor;
 | 
			
		||||
        this.icon = content.icon;
 | 
			
		||||
 | 
			
		||||
        let draggable = DND.makeDraggable(this.actor);
 | 
			
		||||
        draggable.connect('drag-begin',
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.beginItemDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
        draggable.connect('drag-cancelled',
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.cancelledItemDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
        draggable.connect('drag-end',
 | 
			
		||||
                          Lang.bind(this, function() {
 | 
			
		||||
                              Main.overview.endItemDrag(this);
 | 
			
		||||
                          }));
 | 
			
		||||
 | 
			
		||||
        if (!dragSource)
 | 
			
		||||
            // not exactly right, but alignment problems are hard to notice
 | 
			
		||||
            dragSource = content;
 | 
			
		||||
        this._dragActorSource = dragSource;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getDragActorSource: function() {
 | 
			
		||||
        return this._dragActorSource;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getDragActor: function() {
 | 
			
		||||
        return this.metaInfo['createIcon'](Main.overview.dashIconSize);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    shellWorkspaceLaunch: function(params) {
 | 
			
		||||
        if (this.provider.dragActivateResult)
 | 
			
		||||
            this.provider.dragActivateResult(this.metaInfo.id, params);
 | 
			
		||||
        else
 | 
			
		||||
            this.provider.activateResult(this.metaInfo.id, this.terms);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const SearchResultsBase = new Lang.Class({
 | 
			
		||||
    Name: 'SearchResultsBase',
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
 | 
			
		||||
        this._terms = [];
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'search-section',
 | 
			
		||||
                                        vertical: true });
 | 
			
		||||
 | 
			
		||||
        this._resultDisplayBin = new St.Bin({ x_fill: true,
 | 
			
		||||
                                              y_fill: true });
 | 
			
		||||
        this.actor.add(this._resultDisplayBin, { expand: true });
 | 
			
		||||
 | 
			
		||||
        let separator = new Separator.HorizontalSeparator({ style_class: 'search-section-separator' });
 | 
			
		||||
        this.actor.add(separator.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this.actor.destroy();
 | 
			
		||||
        this._terms = [];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearResultDisplay: function() {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    clear: function() {
 | 
			
		||||
        this._clearResultDisplay();
 | 
			
		||||
        this.actor.hide();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _keyFocusIn: function(actor) {
 | 
			
		||||
        this.emit('key-focus-in', actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setMoreIconVisible: function(visible) {
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateSearch: function(providerResults, terms, callback) {
 | 
			
		||||
        this._terms = terms;
 | 
			
		||||
 | 
			
		||||
        if (providerResults.length == 0) {
 | 
			
		||||
            this._clearResultDisplay();
 | 
			
		||||
            this.actor.hide();
 | 
			
		||||
            callback();
 | 
			
		||||
        } else {
 | 
			
		||||
            let maxResults = this._getMaxDisplayedResults();
 | 
			
		||||
            let results = this.provider.filterResults(providerResults, maxResults);
 | 
			
		||||
            let hasMoreResults = results.length < providerResults.length;
 | 
			
		||||
 | 
			
		||||
            this.provider.getResultMetas(results, Lang.bind(this, function(metas) {
 | 
			
		||||
                this.clear();
 | 
			
		||||
 | 
			
		||||
                // To avoid CSS transitions causing flickering when
 | 
			
		||||
                // the first search result stays the same, we hide the
 | 
			
		||||
                // content while filling in the results.
 | 
			
		||||
                this.actor.hide();
 | 
			
		||||
                this._clearResultDisplay();
 | 
			
		||||
                this._renderResults(metas);
 | 
			
		||||
                this._setMoreIconVisible(hasMoreResults && this.provider.canLaunchSearch);
 | 
			
		||||
                this.actor.show();
 | 
			
		||||
                callback();
 | 
			
		||||
            }));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ListSearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'ListSearchResults',
 | 
			
		||||
    Extends: SearchResultsBase,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.parent(provider);
 | 
			
		||||
 | 
			
		||||
        this._container = new St.BoxLayout({ style_class: 'search-section-content' });
 | 
			
		||||
        this.providerIcon = new ProviderIcon(provider);
 | 
			
		||||
        this.providerIcon.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
 | 
			
		||||
        this.providerIcon.connect('clicked', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                provider.launchSearch(this._terms);
 | 
			
		||||
                Main.overview.toggle();
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this._container.add(this.providerIcon, { x_fill: false,
 | 
			
		||||
                                                 y_fill: false,
 | 
			
		||||
                                                 x_align: St.Align.START,
 | 
			
		||||
                                                 y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._content = new St.BoxLayout({ style_class: 'list-search-results',
 | 
			
		||||
                                           vertical: true });
 | 
			
		||||
        this._container.add(this._content, { expand: true });
 | 
			
		||||
 | 
			
		||||
        this._resultDisplayBin.set_child(this._container);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _setMoreIconVisible: function(visible) {
 | 
			
		||||
        this.providerIcon.moreIcon.visible = true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getMaxDisplayedResults: function() {
 | 
			
		||||
        return MAX_LIST_SEARCH_RESULTS_ROWS;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _renderResults: function(metas) {
 | 
			
		||||
        for (let i = 0; i < metas.length; i++) {
 | 
			
		||||
            let display = new ListSearchResult(this.provider, metas[i], this._terms);
 | 
			
		||||
            display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
 | 
			
		||||
            this._content.add_actor(display.actor);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearResultDisplay: function () {
 | 
			
		||||
        this._content.destroy_all_children();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getFirstResult: function() {
 | 
			
		||||
        if (this._content.get_n_children() > 0)
 | 
			
		||||
            return this._content.get_child_at_index(0)._delegate;
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ListSearchResults.prototype);
 | 
			
		||||
 | 
			
		||||
const GridSearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'GridSearchResults',
 | 
			
		||||
    Extends: SearchResultsBase,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.parent(provider);
 | 
			
		||||
 | 
			
		||||
        this._grid = new IconGrid.IconGrid({ rowLimit: MAX_GRID_SEARCH_RESULTS_ROWS,
 | 
			
		||||
                                             xAlign: St.Align.START });
 | 
			
		||||
        this._bin = new St.Bin({ x_align: St.Align.MIDDLE });
 | 
			
		||||
        this._bin.set_child(this._grid.actor);
 | 
			
		||||
 | 
			
		||||
        this._resultDisplayBin.set_child(this._bin);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getMaxDisplayedResults: function() {
 | 
			
		||||
        return this._grid.columnsForWidth(this._bin.width) * this._grid.getRowLimit();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _renderResults: function(metas) {
 | 
			
		||||
        for (let i = 0; i < metas.length; i++) {
 | 
			
		||||
            let display = new GridSearchResult(this.provider, metas[i], this._terms);
 | 
			
		||||
            display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
 | 
			
		||||
            this._grid.addItem(display);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearResultDisplay: function () {
 | 
			
		||||
        this._grid.removeAll();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getFirstResult: function() {
 | 
			
		||||
        if (this._grid.visibleItemsCount() > 0)
 | 
			
		||||
            return this._grid.getItemAtIndex(0)._delegate;
 | 
			
		||||
        else
 | 
			
		||||
            return null;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(GridSearchResults.prototype);
 | 
			
		||||
 | 
			
		||||
const SearchResults = new Lang.Class({
 | 
			
		||||
    Name: 'SearchResults',
 | 
			
		||||
 | 
			
		||||
    _init: function(searchSystem) {
 | 
			
		||||
        this._searchSystem = searchSystem;
 | 
			
		||||
        this._searchSystem.connect('search-updated', Lang.bind(this, this._updateResults));
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.BoxLayout({ name: 'searchResults',
 | 
			
		||||
                                        vertical: true });
 | 
			
		||||
 | 
			
		||||
        this._content = new St.BoxLayout({ name: 'searchResultsContent',
 | 
			
		||||
                                           vertical: true });
 | 
			
		||||
        this._contentBin = new MaxWidthBin({ name: 'searchResultsBin',
 | 
			
		||||
                                             x_fill: true,
 | 
			
		||||
                                             y_fill: true,
 | 
			
		||||
                                             child: this._content });
 | 
			
		||||
 | 
			
		||||
        let scrollChild = new St.BoxLayout();
 | 
			
		||||
        scrollChild.add(this._contentBin, { expand: true });
 | 
			
		||||
 | 
			
		||||
        this._scrollView = new St.ScrollView({ x_fill: true,
 | 
			
		||||
                                               y_fill: false,
 | 
			
		||||
                                               overlay_scrollbars: true,
 | 
			
		||||
                                               style_class: 'search-display vfade' });
 | 
			
		||||
        this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC);
 | 
			
		||||
        this._scrollView.add_actor(scrollChild);
 | 
			
		||||
        let action = new Clutter.PanAction({ interpolate: true });
 | 
			
		||||
        action.connect('pan', Lang.bind(this, this._onPan));
 | 
			
		||||
        this._scrollView.add_action(action);
 | 
			
		||||
 | 
			
		||||
        this.actor.add(this._scrollView, { x_fill: true,
 | 
			
		||||
                                           y_fill: true,
 | 
			
		||||
                                           expand: true,
 | 
			
		||||
                                           x_align: St.Align.START,
 | 
			
		||||
                                           y_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._statusText = new St.Label({ style_class: 'search-statustext' });
 | 
			
		||||
        this._statusBin = new St.Bin({ x_align: St.Align.MIDDLE,
 | 
			
		||||
                                       y_align: St.Align.MIDDLE });
 | 
			
		||||
        this._content.add(this._statusBin, { expand: true });
 | 
			
		||||
        this._statusBin.add_actor(this._statusText);
 | 
			
		||||
        this._providers = this._searchSystem.getProviders();
 | 
			
		||||
        this._providerDisplays = {};
 | 
			
		||||
        for (let i = 0; i < this._providers.length; i++) {
 | 
			
		||||
            this.createProviderDisplay(this._providers[i]);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._highlightDefault = false;
 | 
			
		||||
        this._defaultResult = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onPan: function(action) {
 | 
			
		||||
        let [dist, dx, dy] = action.get_motion_delta(0);
 | 
			
		||||
        let adjustment = this._scrollView.vscroll.adjustment;
 | 
			
		||||
        adjustment.value -= (dy / this.actor.height) * adjustment.page_size;
 | 
			
		||||
        return false;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _keyFocusIn: function(provider, actor) {
 | 
			
		||||
        Util.ensureActorVisibleInScrollView(this._scrollView, actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    createProviderDisplay: function(provider) {
 | 
			
		||||
        let providerDisplay = null;
 | 
			
		||||
 | 
			
		||||
        if (provider.appInfo) {
 | 
			
		||||
            providerDisplay = new ListSearchResults(provider);
 | 
			
		||||
        } else {
 | 
			
		||||
            providerDisplay = new GridSearchResults(provider);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        providerDisplay.connect('key-focus-in', Lang.bind(this, this._keyFocusIn));
 | 
			
		||||
        this._providerDisplays[provider.id] = providerDisplay;
 | 
			
		||||
        this._content.add(providerDisplay.actor);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroyProviderDisplay: function(provider) {
 | 
			
		||||
        this._providerDisplays[provider.id].destroy();
 | 
			
		||||
        delete this._providerDisplays[provider.id];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _clearDisplay: function() {
 | 
			
		||||
        for (let i = 0; i < this._providers.length; i++) {
 | 
			
		||||
            let provider = this._providers[i];
 | 
			
		||||
            let providerDisplay = this._providerDisplays[provider.id];
 | 
			
		||||
            providerDisplay.clear();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    reset: function() {
 | 
			
		||||
        this._searchSystem.reset();
 | 
			
		||||
        this._statusBin.hide();
 | 
			
		||||
        this._clearDisplay();
 | 
			
		||||
        this._defaultResult = null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    startingSearch: function() {
 | 
			
		||||
        this.reset();
 | 
			
		||||
        this._statusText.set_text(_("Searching…"));
 | 
			
		||||
        this._statusBin.show();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _maybeSetInitialSelection: function() {
 | 
			
		||||
        let newDefaultResult = null;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._providers.length; i++) {
 | 
			
		||||
            let provider = this._providers[i];
 | 
			
		||||
            let display = this._providerDisplays[provider.id];
 | 
			
		||||
 | 
			
		||||
            if (!display.actor.visible)
 | 
			
		||||
                continue;
 | 
			
		||||
 | 
			
		||||
            let firstResult = display.getFirstResult();
 | 
			
		||||
            if (firstResult) {
 | 
			
		||||
                newDefaultResult = firstResult;
 | 
			
		||||
                break; // select this one!
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (newDefaultResult != this._defaultResult) {
 | 
			
		||||
            if (this._defaultResult)
 | 
			
		||||
                this._defaultResult.setSelected(false);
 | 
			
		||||
            if (newDefaultResult)
 | 
			
		||||
                newDefaultResult.setSelected(this._highlightDefault);
 | 
			
		||||
 | 
			
		||||
            this._defaultResult = newDefaultResult;
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateStatusText: function () {
 | 
			
		||||
        let haveResults = false;
 | 
			
		||||
 | 
			
		||||
        for (let i = 0; i < this._providers.length; i++) {
 | 
			
		||||
            let provider = this._providers[i];
 | 
			
		||||
            let display = this._providerDisplays[provider.id];
 | 
			
		||||
            if (display.getFirstResult()) {
 | 
			
		||||
                haveResults = true;
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (!haveResults) {
 | 
			
		||||
            this._statusText.set_text(_("No results."));
 | 
			
		||||
            this._statusBin.show();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._statusBin.hide();
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateResults: function(searchSystem, results) {
 | 
			
		||||
        let terms = searchSystem.getTerms();
 | 
			
		||||
        let [provider, providerResults] = results;
 | 
			
		||||
        let display = this._providerDisplays[provider.id];
 | 
			
		||||
 | 
			
		||||
        display.updateSearch(providerResults, terms, Lang.bind(this, function() {
 | 
			
		||||
            this._maybeSetInitialSelection();
 | 
			
		||||
            this._updateStatusText();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    activateDefault: function() {
 | 
			
		||||
        if (this._defaultResult)
 | 
			
		||||
            this._defaultResult.activate();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    highlightDefault: function(highlight) {
 | 
			
		||||
        this._highlightDefault = highlight;
 | 
			
		||||
        if (this._defaultResult)
 | 
			
		||||
            this._defaultResult.setSelected(highlight);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    navigateFocus: function(direction) {
 | 
			
		||||
        let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL;
 | 
			
		||||
        if (direction == Gtk.DirectionType.TAB_BACKWARD ||
 | 
			
		||||
            direction == (rtl ? Gtk.DirectionType.RIGHT
 | 
			
		||||
                              : Gtk.DirectionType.LEFT) ||
 | 
			
		||||
            direction == Gtk.DirectionType.UP) {
 | 
			
		||||
            this.actor.navigate_focus(null, direction, false);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let from = this._defaultResult ? this._defaultResult.actor : null;
 | 
			
		||||
        this.actor.navigate_focus(from, direction, false);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ProviderIcon = new Lang.Class({
 | 
			
		||||
    Name: 'ProviderIcon',
 | 
			
		||||
    Extends: St.Button,
 | 
			
		||||
 | 
			
		||||
    PROVIDER_ICON_SIZE: 48,
 | 
			
		||||
 | 
			
		||||
    _init: function(provider) {
 | 
			
		||||
        this.provider = provider;
 | 
			
		||||
        this.parent({ style_class: 'search-provider-icon',
 | 
			
		||||
                      reactive: true,
 | 
			
		||||
                      can_focus: true,
 | 
			
		||||
                      accessible_name: provider.appInfo.get_name(),
 | 
			
		||||
                      track_hover: true });
 | 
			
		||||
 | 
			
		||||
        this._content = new St.Widget({ layout_manager: new Clutter.BinLayout() });
 | 
			
		||||
        this.set_child(this._content);
 | 
			
		||||
 | 
			
		||||
        let rtl = (this.get_text_direction() == Clutter.TextDirection.RTL);
 | 
			
		||||
 | 
			
		||||
        this.moreIcon = new St.Widget({ style_class: 'search-provider-icon-more',
 | 
			
		||||
                                        visible: false,
 | 
			
		||||
                                        x_align: rtl ? Clutter.ActorAlign.START : Clutter.ActorAlign.END,
 | 
			
		||||
                                        y_align: Clutter.ActorAlign.END,
 | 
			
		||||
                                        x_expand: true,
 | 
			
		||||
                                        y_expand: true });
 | 
			
		||||
 | 
			
		||||
        let icon = new St.Icon({ icon_size: this.PROVIDER_ICON_SIZE,
 | 
			
		||||
                                 gicon: provider.appInfo.get_icon() });
 | 
			
		||||
        this._content.add_actor(icon);
 | 
			
		||||
        this._content.add_actor(this.moreIcon);
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
@@ -10,6 +10,8 @@ const FileUtils = imports.misc.fileUtils;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Params = imports.misc.params;
 | 
			
		||||
 | 
			
		||||
const Config = imports.misc.config;
 | 
			
		||||
 | 
			
		||||
const DEFAULT_MODE = 'restrictive';
 | 
			
		||||
 | 
			
		||||
const _modes = {
 | 
			
		||||
@@ -92,8 +94,12 @@ const _modes = {
 | 
			
		||||
        isLocked: false,
 | 
			
		||||
        isPrimary: true,
 | 
			
		||||
        unlockDialog: imports.ui.unlockDialog.UnlockDialog,
 | 
			
		||||
        components: ['networkAgent', 'polkitAgent', 'telepathyClient',
 | 
			
		||||
        components: Config.HAVE_NETWORKMANAGER ?
 | 
			
		||||
                    ['networkAgent', 'polkitAgent', 'telepathyClient',
 | 
			
		||||
                     'keyring', 'autorunManager', 'automountManager'] :
 | 
			
		||||
                    ['polkitAgent', 'telepathyClient',
 | 
			
		||||
                     'keyring', 'autorunManager', 'automountManager'],
 | 
			
		||||
 | 
			
		||||
        panel: {
 | 
			
		||||
            left: ['activities', 'appMenu'],
 | 
			
		||||
            center: ['dateMenu'],
 | 
			
		||||
@@ -102,19 +108,12 @@ const _modes = {
 | 
			
		||||
    }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function _getModes(modesLoadedCallback) {
 | 
			
		||||
    FileUtils.collectFromDatadirsAsync('modes',
 | 
			
		||||
                                       { processFile: _loadMode,
 | 
			
		||||
                                         loadedCallback: modesLoadedCallback,
 | 
			
		||||
                                         data: _modes });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _loadMode(file, info, loadedData) {
 | 
			
		||||
function _loadMode(file, info) {
 | 
			
		||||
    let name = info.get_name();
 | 
			
		||||
    let suffix = name.indexOf('.json');
 | 
			
		||||
    let modeName = suffix == -1 ? name : name.slice(name, suffix);
 | 
			
		||||
 | 
			
		||||
    if (loadedData.hasOwnProperty(modeName))
 | 
			
		||||
    if (_modes.hasOwnProperty(modeName))
 | 
			
		||||
        return;
 | 
			
		||||
 | 
			
		||||
    let fileContent, success, tag, newMode;
 | 
			
		||||
@@ -125,19 +124,24 @@ function _loadMode(file, info, loadedData) {
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    loadedData[modeName] = {};
 | 
			
		||||
    _modes[modeName] = {};
 | 
			
		||||
    let propBlacklist = ['unlockDialog'];
 | 
			
		||||
    for (let prop in loadedData[DEFAULT_MODE]) {
 | 
			
		||||
    for (let prop in _modes[DEFAULT_MODE]) {
 | 
			
		||||
        if (newMode[prop] !== undefined &&
 | 
			
		||||
            propBlacklist.indexOf(prop) == -1)
 | 
			
		||||
            loadedData[modeName][prop]= newMode[prop];
 | 
			
		||||
            _modes[modeName][prop] = newMode[prop];
 | 
			
		||||
    }
 | 
			
		||||
    loadedData[modeName]['isPrimary'] = true;
 | 
			
		||||
    _modes[modeName]['isPrimary'] = true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function _loadModes() {
 | 
			
		||||
    FileUtils.collectFromDatadirs('modes', false, _loadMode);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function listModes() {
 | 
			
		||||
    _getModes(function(modes) {
 | 
			
		||||
        let names = Object.getOwnPropertyNames(modes);
 | 
			
		||||
    _loadModes();
 | 
			
		||||
    Mainloop.idle_add(function() {
 | 
			
		||||
        let names = Object.getOwnPropertyNames(_modes);
 | 
			
		||||
        for (let i = 0; i < names.length; i++)
 | 
			
		||||
            if (_modes[names[i]].isPrimary)
 | 
			
		||||
                print(names[i]);
 | 
			
		||||
@@ -149,17 +153,13 @@ function listModes() {
 | 
			
		||||
const SessionMode = new Lang.Class({
 | 
			
		||||
    Name: 'SessionMode',
 | 
			
		||||
 | 
			
		||||
    init: function() {
 | 
			
		||||
        _getModes(Lang.bind(this, function(modes) {
 | 
			
		||||
            this._modes = modes;
 | 
			
		||||
            let primary = modes[global.session_mode] &&
 | 
			
		||||
                          modes[global.session_mode].isPrimary;
 | 
			
		||||
            let mode = primary ? global.session_mode : 'user';
 | 
			
		||||
            this._modeStack = [mode];
 | 
			
		||||
            this._sync();
 | 
			
		||||
 | 
			
		||||
            this.emit('sessions-loaded');
 | 
			
		||||
        }));
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        _loadModes();
 | 
			
		||||
        let isPrimary = (_modes[global.session_mode] &&
 | 
			
		||||
                         _modes[global.session_mode].isPrimary);
 | 
			
		||||
        let mode = isPrimary ? global.session_mode : 'user';
 | 
			
		||||
        this._modeStack = [mode];
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    pushMode: function(mode) {
 | 
			
		||||
@@ -186,13 +186,13 @@ const SessionMode = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let params = this._modes[this.currentMode];
 | 
			
		||||
        let params = _modes[this.currentMode];
 | 
			
		||||
        let defaults;
 | 
			
		||||
        if (params.parentMode)
 | 
			
		||||
            defaults = Params.parse(this._modes[params.parentMode],
 | 
			
		||||
                                    this._modes[DEFAULT_MODE]);
 | 
			
		||||
            defaults = Params.parse(_modes[params.parentMode],
 | 
			
		||||
                                    _modes[DEFAULT_MODE]);
 | 
			
		||||
        else
 | 
			
		||||
            defaults = this._modes[DEFAULT_MODE];
 | 
			
		||||
            defaults = _modes[DEFAULT_MODE];
 | 
			
		||||
        params = Params.parse(params, defaults);
 | 
			
		||||
 | 
			
		||||
        // A simplified version of Lang.copyProperties, handles
 | 
			
		||||
 
 | 
			
		||||
@@ -10,64 +10,68 @@ const Config = imports.misc.config;
 | 
			
		||||
const ExtensionSystem = imports.ui.extensionSystem;
 | 
			
		||||
const ExtensionDownloader = imports.ui.extensionDownloader;
 | 
			
		||||
const ExtensionUtils = imports.misc.extensionUtils;
 | 
			
		||||
const Hash = imports.misc.hash;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const Screenshot = imports.ui.screenshot;
 | 
			
		||||
const ViewSelector = imports.ui.viewSelector;
 | 
			
		||||
 | 
			
		||||
const GnomeShellIface = <interface name="org.gnome.Shell">
 | 
			
		||||
<method name="Eval">
 | 
			
		||||
    <arg type="s" direction="in" name="script" />
 | 
			
		||||
    <arg type="b" direction="out" name="success" />
 | 
			
		||||
    <arg type="s" direction="out" name="result" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="FocusSearch"/>
 | 
			
		||||
<method name="ShowOSD">
 | 
			
		||||
    <arg type="a{sv}" direction="in" name="params"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="FocusApp">
 | 
			
		||||
    <arg type="s" direction="in" name="id"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="ShowApplications" />
 | 
			
		||||
<method name="GrabAccelerator">
 | 
			
		||||
    <arg type="s" direction="in" name="accelerator"/>
 | 
			
		||||
    <arg type="u" direction="in" name="flags"/>
 | 
			
		||||
    <arg type="u" direction="out" name="action"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GrabAccelerators">
 | 
			
		||||
    <arg type="a(su)" direction="in" name="accelerators"/>
 | 
			
		||||
    <arg type="au" direction="out" name="actions"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="UngrabAccelerator">
 | 
			
		||||
    <arg type="u" direction="in" name="action"/>
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
</method>
 | 
			
		||||
<signal name="AcceleratorActivated">
 | 
			
		||||
    <arg name="action" type="u" />
 | 
			
		||||
    <arg name="deviceid" type="u" />
 | 
			
		||||
    <arg name="timestamp" type="u" />
 | 
			
		||||
</signal>
 | 
			
		||||
<property name="Mode" type="s" access="read" />
 | 
			
		||||
<property name="OverviewActive" type="b" access="readwrite" />
 | 
			
		||||
<property name="ShellVersion" type="s" access="read" />
 | 
			
		||||
</interface>;
 | 
			
		||||
const GnomeShellIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.Shell"> \
 | 
			
		||||
<method name="Eval"> \
 | 
			
		||||
    <arg type="s" direction="in" name="script" /> \
 | 
			
		||||
    <arg type="b" direction="out" name="success" /> \
 | 
			
		||||
    <arg type="s" direction="out" name="result" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="FocusSearch"/> \
 | 
			
		||||
<method name="ShowOSD"> \
 | 
			
		||||
    <arg type="a{sv}" direction="in" name="params"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="FocusApp"> \
 | 
			
		||||
    <arg type="s" direction="in" name="id"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="ShowApplications" /> \
 | 
			
		||||
<method name="GrabAccelerator"> \
 | 
			
		||||
    <arg type="s" direction="in" name="accelerator"/> \
 | 
			
		||||
    <arg type="u" direction="in" name="flags"/> \
 | 
			
		||||
    <arg type="u" direction="out" name="action"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="GrabAccelerators"> \
 | 
			
		||||
    <arg type="a(su)" direction="in" name="accelerators"/> \
 | 
			
		||||
    <arg type="au" direction="out" name="actions"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="UngrabAccelerator"> \
 | 
			
		||||
    <arg type="u" direction="in" name="action"/> \
 | 
			
		||||
    <arg type="b" direction="out" name="success"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<signal name="AcceleratorActivated"> \
 | 
			
		||||
    <arg name="action" type="u" /> \
 | 
			
		||||
    <arg name="deviceid" type="u" /> \
 | 
			
		||||
    <arg name="timestamp" type="u" /> \
 | 
			
		||||
</signal> \
 | 
			
		||||
<property name="Mode" type="s" access="read" /> \
 | 
			
		||||
<property name="OverviewActive" type="b" access="readwrite" /> \
 | 
			
		||||
<property name="ShellVersion" type="s" access="read" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const ScreenSaverIface = <interface name="org.gnome.ScreenSaver">
 | 
			
		||||
<method name="Lock">
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetActive">
 | 
			
		||||
    <arg name="active" direction="out" type="b" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="SetActive">
 | 
			
		||||
    <arg name="value" direction="in" type="b" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetActiveTime">
 | 
			
		||||
    <arg name="value" direction="out" type="u" />
 | 
			
		||||
</method>
 | 
			
		||||
<signal name="ActiveChanged">
 | 
			
		||||
    <arg name="new_value" type="b" />
 | 
			
		||||
</signal>
 | 
			
		||||
</interface>;
 | 
			
		||||
const ScreenSaverIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.ScreenSaver"> \
 | 
			
		||||
<method name="Lock"> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="GetActive"> \
 | 
			
		||||
    <arg name="active" direction="out" type="b" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="SetActive"> \
 | 
			
		||||
    <arg name="value" direction="in" type="b" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="GetActiveTime"> \
 | 
			
		||||
    <arg name="value" direction="out" type="u" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<signal name="ActiveChanged"> \
 | 
			
		||||
    <arg name="new_value" type="b" /> \
 | 
			
		||||
</signal> \
 | 
			
		||||
<signal name="WakeUpScreen" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const GnomeShell = new Lang.Class({
 | 
			
		||||
    Name: 'GnomeShellDBus',
 | 
			
		||||
@@ -79,8 +83,8 @@ const GnomeShell = new Lang.Class({
 | 
			
		||||
        this._extensionsService = new GnomeShellExtensions();
 | 
			
		||||
        this._screenshotService = new Screenshot.ScreenshotService();
 | 
			
		||||
 | 
			
		||||
        this._grabbedAccelerators = new Hash.Map();
 | 
			
		||||
        this._grabbers = new Hash.Map();
 | 
			
		||||
        this._grabbedAccelerators = new Map();
 | 
			
		||||
        this._grabbers = new Map();
 | 
			
		||||
 | 
			
		||||
        global.display.connect('accelerator-activated', Lang.bind(this,
 | 
			
		||||
            function(display, action, deviceid, timestamp) {
 | 
			
		||||
@@ -115,7 +119,7 @@ const GnomeShell = new Lang.Class({
 | 
			
		||||
                returnValue = '';
 | 
			
		||||
            success = true;
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
            returnValue = JSON.stringify(e);
 | 
			
		||||
            returnValue = '' + e;
 | 
			
		||||
            success = false;
 | 
			
		||||
        }
 | 
			
		||||
        return [success, returnValue];
 | 
			
		||||
@@ -129,11 +133,16 @@ const GnomeShell = new Lang.Class({
 | 
			
		||||
        for (let param in params)
 | 
			
		||||
            params[param] = params[param].deep_unpack();
 | 
			
		||||
 | 
			
		||||
        let monitorIndex = -1;
 | 
			
		||||
        if (params['monitor'])
 | 
			
		||||
            monitorIndex = params['monitor'];
 | 
			
		||||
 | 
			
		||||
        let icon = null;
 | 
			
		||||
        if (params['icon'])
 | 
			
		||||
            icon = Gio.Icon.new_for_string(params['icon']);
 | 
			
		||||
 | 
			
		||||
        Main.osdWindow.setIcon(icon);
 | 
			
		||||
        Main.osdWindow.setMonitor (monitorIndex);
 | 
			
		||||
        Main.osdWindow.setLabel(params['label']);
 | 
			
		||||
        Main.osdWindow.setLevel(params['level']);
 | 
			
		||||
 | 
			
		||||
@@ -219,9 +228,8 @@ const GnomeShell = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onGrabberBusNameVanished: function(connection, name) {
 | 
			
		||||
        let grabs = this._grabbedAccelerators.items();
 | 
			
		||||
        for (let i = 0; i < grabs.length; i++) {
 | 
			
		||||
            let [action, sender] = grabs[i];
 | 
			
		||||
        let grabs = this._grabbedAccelerators.entries();
 | 
			
		||||
        for (let [action, sender] of grabs) {
 | 
			
		||||
            if (sender == name)
 | 
			
		||||
                this._ungrabAccelerator(action);
 | 
			
		||||
        }
 | 
			
		||||
@@ -246,41 +254,43 @@ const GnomeShell = new Lang.Class({
 | 
			
		||||
    ShellVersion: Config.PACKAGE_VERSION
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const GnomeShellExtensionsIface = <interface name="org.gnome.Shell.Extensions">
 | 
			
		||||
<method name="ListExtensions">
 | 
			
		||||
    <arg type="a{sa{sv}}" direction="out" name="extensions" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetExtensionInfo">
 | 
			
		||||
    <arg type="s" direction="in" name="extension" />
 | 
			
		||||
    <arg type="a{sv}" direction="out" name="info" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetExtensionErrors">
 | 
			
		||||
    <arg type="s" direction="in" name="extension" />
 | 
			
		||||
    <arg type="as" direction="out" name="errors" />
 | 
			
		||||
</method>
 | 
			
		||||
<signal name="ExtensionStatusChanged">
 | 
			
		||||
    <arg type="s" name="uuid"/>
 | 
			
		||||
    <arg type="i" name="state"/>
 | 
			
		||||
    <arg type="s" name="error"/>
 | 
			
		||||
</signal>
 | 
			
		||||
<method name="InstallRemoteExtension">
 | 
			
		||||
    <arg type="s" direction="in" name="uuid"/>
 | 
			
		||||
    <arg type="s" direction="out" name="result"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="UninstallExtension">
 | 
			
		||||
    <arg type="s" direction="in" name="uuid"/>
 | 
			
		||||
    <arg type="b" direction="out" name="success"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="LaunchExtensionPrefs">
 | 
			
		||||
    <arg type="s" direction="in" name="uuid"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="ReloadExtension">
 | 
			
		||||
    <arg type="s" direction="in" name="uuid"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="CheckForUpdates">
 | 
			
		||||
</method>
 | 
			
		||||
<property name="ShellVersion" type="s" access="read" />
 | 
			
		||||
</interface>;
 | 
			
		||||
const GnomeShellExtensionsIface = '<node> \
 | 
			
		||||
<interface name="org.gnome.Shell.Extensions"> \
 | 
			
		||||
<method name="ListExtensions"> \
 | 
			
		||||
    <arg type="a{sa{sv}}" direction="out" name="extensions" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="GetExtensionInfo"> \
 | 
			
		||||
    <arg type="s" direction="in" name="extension" /> \
 | 
			
		||||
    <arg type="a{sv}" direction="out" name="info" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="GetExtensionErrors"> \
 | 
			
		||||
    <arg type="s" direction="in" name="extension" /> \
 | 
			
		||||
    <arg type="as" direction="out" name="errors" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
<signal name="ExtensionStatusChanged"> \
 | 
			
		||||
    <arg type="s" name="uuid"/> \
 | 
			
		||||
    <arg type="i" name="state"/> \
 | 
			
		||||
    <arg type="s" name="error"/> \
 | 
			
		||||
</signal> \
 | 
			
		||||
<method name="InstallRemoteExtension"> \
 | 
			
		||||
    <arg type="s" direction="in" name="uuid"/> \
 | 
			
		||||
    <arg type="s" direction="out" name="result"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="UninstallExtension"> \
 | 
			
		||||
    <arg type="s" direction="in" name="uuid"/> \
 | 
			
		||||
    <arg type="b" direction="out" name="success"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="LaunchExtensionPrefs"> \
 | 
			
		||||
    <arg type="s" direction="in" name="uuid"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="ReloadExtension"> \
 | 
			
		||||
    <arg type="s" direction="in" name="uuid"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="CheckForUpdates"> \
 | 
			
		||||
</method> \
 | 
			
		||||
<property name="ShellVersion" type="s" access="read" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const GnomeShellExtensions = new Lang.Class({
 | 
			
		||||
    Name: 'GnomeShellExtensionsDBus',
 | 
			
		||||
@@ -362,8 +372,10 @@ const GnomeShellExtensions = new Lang.Class({
 | 
			
		||||
    LaunchExtensionPrefs: function(uuid) {
 | 
			
		||||
        let appSys = Shell.AppSystem.get_default();
 | 
			
		||||
        let app = appSys.lookup_app('gnome-shell-extension-prefs.desktop');
 | 
			
		||||
        app.launch(global.display.get_current_time_roundtrip(),
 | 
			
		||||
                   ['extension:///' + uuid], -1, null);
 | 
			
		||||
        let info = app.get_app_info();
 | 
			
		||||
        let timestamp = global.display.get_current_time_roundtrip();
 | 
			
		||||
        info.launch_uris(['extension:///' + uuid],
 | 
			
		||||
                         global.create_app_launch_context(timestamp, -1));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    ReloadExtension: function(uuid) {
 | 
			
		||||
@@ -396,6 +408,9 @@ const ScreenSaverDBus = new Lang.Class({
 | 
			
		||||
        screenShield.connect('active-changed', Lang.bind(this, function(shield) {
 | 
			
		||||
            this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.active]));
 | 
			
		||||
        }));
 | 
			
		||||
        screenShield.connect('wake-up-screen', Lang.bind(this, function(shield) {
 | 
			
		||||
            this._dbusImpl.emit_signal('WakeUpScreen', null);
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this);
 | 
			
		||||
        this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver');
 | 
			
		||||
 
 | 
			
		||||
@@ -132,14 +132,14 @@ function _setMenuAlignment(entry, stageX) {
 | 
			
		||||
function _onButtonPressEvent(actor, event, entry) {
 | 
			
		||||
    if (entry.menu.isOpen) {
 | 
			
		||||
        entry.menu.close(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    } else if (event.get_button() == 3) {
 | 
			
		||||
        let [stageX, stageY] = event.get_coords();
 | 
			
		||||
        _setMenuAlignment(entry, stageX);
 | 
			
		||||
        entry.menu.open(BoxPointer.PopupAnimation.FULL);
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
    return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
function _onPopup(actor, entry) {
 | 
			
		||||
 
 | 
			
		||||
@@ -521,36 +521,38 @@ const ShellProcessesDialog = new Lang.Class({
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(ShellProcessesDialog.prototype);
 | 
			
		||||
 | 
			
		||||
const GnomeShellMountOpIface = <interface name="org.Gtk.MountOperationHandler">
 | 
			
		||||
<method name="AskPassword">
 | 
			
		||||
    <arg type="s" direction="in" name="object_id"/>
 | 
			
		||||
    <arg type="s" direction="in" name="message"/>
 | 
			
		||||
    <arg type="s" direction="in" name="icon_name"/>
 | 
			
		||||
    <arg type="s" direction="in" name="default_user"/>
 | 
			
		||||
    <arg type="s" direction="in" name="default_domain"/>
 | 
			
		||||
    <arg type="u" direction="in" name="flags"/>
 | 
			
		||||
    <arg type="u" direction="out" name="response"/>
 | 
			
		||||
    <arg type="a{sv}" direction="out" name="response_details"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="AskQuestion">
 | 
			
		||||
    <arg type="s" direction="in" name="object_id"/>
 | 
			
		||||
    <arg type="s" direction="in" name="message"/>
 | 
			
		||||
    <arg type="s" direction="in" name="icon_name"/>
 | 
			
		||||
    <arg type="as" direction="in" name="choices"/>
 | 
			
		||||
    <arg type="u" direction="out" name="response"/>
 | 
			
		||||
    <arg type="a{sv}" direction="out" name="response_details"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="ShowProcesses">
 | 
			
		||||
    <arg type="s" direction="in" name="object_id"/>
 | 
			
		||||
    <arg type="s" direction="in" name="message"/>
 | 
			
		||||
    <arg type="s" direction="in" name="icon_name"/>
 | 
			
		||||
    <arg type="ai" direction="in" name="application_pids"/>
 | 
			
		||||
    <arg type="as" direction="in" name="choices"/>
 | 
			
		||||
    <arg type="u" direction="out" name="response"/>
 | 
			
		||||
    <arg type="a{sv}" direction="out" name="response_details"/>
 | 
			
		||||
</method>
 | 
			
		||||
<method name="Close"/>
 | 
			
		||||
</interface>;
 | 
			
		||||
const GnomeShellMountOpIface = '<node> \
 | 
			
		||||
<interface name="org.Gtk.MountOperationHandler"> \
 | 
			
		||||
<method name="AskPassword"> \
 | 
			
		||||
    <arg type="s" direction="in" name="object_id"/> \
 | 
			
		||||
    <arg type="s" direction="in" name="message"/> \
 | 
			
		||||
    <arg type="s" direction="in" name="icon_name"/> \
 | 
			
		||||
    <arg type="s" direction="in" name="default_user"/> \
 | 
			
		||||
    <arg type="s" direction="in" name="default_domain"/> \
 | 
			
		||||
    <arg type="u" direction="in" name="flags"/> \
 | 
			
		||||
    <arg type="u" direction="out" name="response"/> \
 | 
			
		||||
    <arg type="a{sv}" direction="out" name="response_details"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="AskQuestion"> \
 | 
			
		||||
    <arg type="s" direction="in" name="object_id"/> \
 | 
			
		||||
    <arg type="s" direction="in" name="message"/> \
 | 
			
		||||
    <arg type="s" direction="in" name="icon_name"/> \
 | 
			
		||||
    <arg type="as" direction="in" name="choices"/> \
 | 
			
		||||
    <arg type="u" direction="out" name="response"/> \
 | 
			
		||||
    <arg type="a{sv}" direction="out" name="response_details"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="ShowProcesses"> \
 | 
			
		||||
    <arg type="s" direction="in" name="object_id"/> \
 | 
			
		||||
    <arg type="s" direction="in" name="message"/> \
 | 
			
		||||
    <arg type="s" direction="in" name="icon_name"/> \
 | 
			
		||||
    <arg type="ai" direction="in" name="application_pids"/> \
 | 
			
		||||
    <arg type="as" direction="in" name="choices"/> \
 | 
			
		||||
    <arg type="u" direction="out" name="response"/> \
 | 
			
		||||
    <arg type="a{sv}" direction="out" name="response_details"/> \
 | 
			
		||||
</method> \
 | 
			
		||||
<method name="Close"/> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const ShellMountOperationType = {
 | 
			
		||||
    NONE: 0,
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
/* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */
 | 
			
		||||
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
const Cairo = imports.cairo;
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Atk = imports.gi.Atk;
 | 
			
		||||
 | 
			
		||||
const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */
 | 
			
		||||
 | 
			
		||||
@@ -111,12 +111,12 @@ const Slider = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _startDragging: function(actor, event) {
 | 
			
		||||
        this.startDragging(event);
 | 
			
		||||
        return this.startDragging(event);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    startDragging: function(event) {
 | 
			
		||||
        if (this._dragging)
 | 
			
		||||
            return false;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        this._dragging = true;
 | 
			
		||||
 | 
			
		||||
@@ -129,7 +129,7 @@ const Slider = new Lang.Class({
 | 
			
		||||
        let absX, absY;
 | 
			
		||||
        [absX, absY] = event.get_coords();
 | 
			
		||||
        this._moveHandle(absX, absY);
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _endDragging: function() {
 | 
			
		||||
@@ -143,7 +143,7 @@ const Slider = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
            this.emit('drag-end');
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    scroll: function(event) {
 | 
			
		||||
@@ -151,7 +151,7 @@ const Slider = new Lang.Class({
 | 
			
		||||
        let delta;
 | 
			
		||||
 | 
			
		||||
        if (event.is_pointer_emulated())
 | 
			
		||||
            return;
 | 
			
		||||
            return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
 | 
			
		||||
        if (direction == Clutter.ScrollDirection.DOWN) {
 | 
			
		||||
            delta = -SLIDER_SCROLL_STEP;
 | 
			
		||||
@@ -168,17 +168,18 @@ const Slider = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.queue_repaint();
 | 
			
		||||
        this.emit('value-changed', this._value);
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onScrollEvent: function(actor, event) {
 | 
			
		||||
        this.scroll(event);
 | 
			
		||||
        return this.scroll(event);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _motionEvent: function(actor, event) {
 | 
			
		||||
        let absX, absY;
 | 
			
		||||
        [absX, absY] = event.get_coords();
 | 
			
		||||
        this._moveHandle(absX, absY);
 | 
			
		||||
        return true;
 | 
			
		||||
        return Clutter.EVENT_STOP;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    onKeyPressEvent: function (actor, event) {
 | 
			
		||||
@@ -189,9 +190,9 @@ const Slider = new Lang.Class({
 | 
			
		||||
            this.actor.queue_repaint();
 | 
			
		||||
            this.emit('value-changed', this._value);
 | 
			
		||||
            this.emit('drag-end');
 | 
			
		||||
            return true;
 | 
			
		||||
            return Clutter.EVENT_STOP;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
        return Clutter.EVENT_PROPAGATE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _moveHandle: function(absX, absY) {
 | 
			
		||||
 
 | 
			
		||||
@@ -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 St = imports.gi.St;
 | 
			
		||||
@@ -43,7 +44,7 @@ const ATIndicator = new Lang.Class({
 | 
			
		||||
        this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
 | 
			
		||||
        this._hbox.add_child(new St.Icon({ style_class: 'system-status-icon',
 | 
			
		||||
                                           icon_name: 'preferences-desktop-accessibility-symbolic' }));
 | 
			
		||||
        this._hbox.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
 | 
			
		||||
        this._hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
 | 
			
		||||
 | 
			
		||||
        this.actor.add_child(this._hbox);
 | 
			
		||||
 | 
			
		||||
@@ -94,7 +95,7 @@ const ATIndicator = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this.actor.visible = alwaysShow || items.some(function(f) { return !!f.state; });
 | 
			
		||||
 | 
			
		||||
        return false;
 | 
			
		||||
        return GLib.SOURCE_REMOVE;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _queueSyncMenuVisibility: function() {
 | 
			
		||||
 
 | 
			
		||||
@@ -2,16 +2,27 @@
 | 
			
		||||
 | 
			
		||||
const Clutter = imports.gi.Clutter;
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const GnomeBluetooth = imports.gi.GnomeBluetooth;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill';
 | 
			
		||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill';
 | 
			
		||||
 | 
			
		||||
const RfkillManagerInterface = '<node> \
 | 
			
		||||
<interface name="org.gnome.SettingsDaemon.Rfkill"> \
 | 
			
		||||
<property name="BluetoothAirplaneMode" type="b" access="readwrite" /> \
 | 
			
		||||
<property name="BluetoothHasAirplaneMode" type="b" access="read" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface);
 | 
			
		||||
 | 
			
		||||
const Indicator = new Lang.Class({
 | 
			
		||||
    Name: 'BTIndicator',
 | 
			
		||||
    Extends: PanelMenu.SystemIndicator,
 | 
			
		||||
@@ -22,237 +33,76 @@ const Indicator = new Lang.Class({
 | 
			
		||||
        this._indicator = this._addIndicator();
 | 
			
		||||
        this._indicator.icon_name = 'bluetooth-active-symbolic';
 | 
			
		||||
 | 
			
		||||
        this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
 | 
			
		||||
                                             Lang.bind(this, function(proxy, error) {
 | 
			
		||||
                                                 if (error) {
 | 
			
		||||
                                                     log(error.message);
 | 
			
		||||
                                                     return;
 | 
			
		||||
                                                 }
 | 
			
		||||
 | 
			
		||||
                                                 this._sync();
 | 
			
		||||
                                             }));
 | 
			
		||||
        this._proxy.connect('g-properties-changed', Lang.bind(this, this._sync));
 | 
			
		||||
 | 
			
		||||
        // The Bluetooth menu only appears when Bluetooth is in use,
 | 
			
		||||
        // so just statically build it with a "Turn Off" menu item.
 | 
			
		||||
        this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true);
 | 
			
		||||
        this._item.icon.icon_name = 'bluetooth-active-symbolic';
 | 
			
		||||
        this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
 | 
			
		||||
            this._applet.killswitch_state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED;
 | 
			
		||||
            this._proxy.BluetoothAirplaneMode = true;
 | 
			
		||||
        }));
 | 
			
		||||
        this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop');
 | 
			
		||||
        this.menu.addMenuItem(this._item);
 | 
			
		||||
 | 
			
		||||
        this._applet = new GnomeBluetoothApplet.Applet();
 | 
			
		||||
        this._applet.connect('devices-changed', Lang.bind(this, this._sync));
 | 
			
		||||
        this._client = new GnomeBluetooth.Client();
 | 
			
		||||
        this._model = this._client.get_model();
 | 
			
		||||
        this._model.connect('row-changed', Lang.bind(this, this._sync));
 | 
			
		||||
        this._model.connect('row-deleted', Lang.bind(this, this._sync));
 | 
			
		||||
        this._model.connect('row-inserted', Lang.bind(this, this._sync));
 | 
			
		||||
        Main.sessionMode.connect('updated', Lang.bind(this, this._sync));
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
        this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest));
 | 
			
		||||
        this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest));
 | 
			
		||||
        this._applet.connect('auth-request', Lang.bind(this, this._authRequest));
 | 
			
		||||
        this._applet.connect('auth-service-request', Lang.bind(this, this._authServiceRequest));
 | 
			
		||||
        this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest));
 | 
			
		||||
    _getDefaultAdapter: function() {
 | 
			
		||||
        let [ret, iter] = this._model.get_iter_first();
 | 
			
		||||
        while (ret) {
 | 
			
		||||
            let isDefault = this._model.get_value(iter,
 | 
			
		||||
                                                  GnomeBluetooth.Column.DEFAULT);
 | 
			
		||||
            if (isDefault)
 | 
			
		||||
                return iter;
 | 
			
		||||
            ret = this._model.iter_next(iter);
 | 
			
		||||
        }
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getNConnectedDevices: function() {
 | 
			
		||||
        let adapter = this._getDefaultAdapter();
 | 
			
		||||
        if (!adapter)
 | 
			
		||||
            return 0;
 | 
			
		||||
 | 
			
		||||
        let nDevices = 0;
 | 
			
		||||
        let [ret, iter] = this._model.iter_children(adapter);
 | 
			
		||||
        while (ret) {
 | 
			
		||||
            let isConnected = this._model.get_value(iter,
 | 
			
		||||
                                                    GnomeBluetooth.Column.CONNECTED);
 | 
			
		||||
            if (isConnected)
 | 
			
		||||
                nDevices++;
 | 
			
		||||
            ret = this._model.iter_next(iter);
 | 
			
		||||
        }
 | 
			
		||||
        return nDevices;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let connectedDevices = this._applet.get_devices().filter(function(device) {
 | 
			
		||||
            return device.connected;
 | 
			
		||||
        });
 | 
			
		||||
        let nDevices = connectedDevices.length;
 | 
			
		||||
        let nDevices = this._getNConnectedDevices();
 | 
			
		||||
        let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
 | 
			
		||||
 | 
			
		||||
        let on = nDevices > 0;
 | 
			
		||||
        this._indicator.visible = on;
 | 
			
		||||
        this._item.actor.visible = on;
 | 
			
		||||
        this.menu.setSensitive(sensitive);
 | 
			
		||||
        this._indicator.visible = nDevices > 0;
 | 
			
		||||
        this._item.actor.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode;
 | 
			
		||||
 | 
			
		||||
        if (on)
 | 
			
		||||
        if (nDevices > 0)
 | 
			
		||||
            this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices", nDevices).format(nDevices);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _ensureSource: function() {
 | 
			
		||||
        if (!this._source) {
 | 
			
		||||
            this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active');
 | 
			
		||||
            this._source.policy = new MessageTray.NotificationApplicationPolicy('gnome-bluetooth-panel');
 | 
			
		||||
            Main.messageTray.add(this._source);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _authRequest: function(applet, device_path, name, long_name) {
 | 
			
		||||
        this._ensureSource();
 | 
			
		||||
        this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _authServiceRequest: function(applet, device_path, name, long_name, uuid) {
 | 
			
		||||
        this._ensureSource();
 | 
			
		||||
        this._source.notify(new AuthServiceNotification(this._source, this._applet, device_path, name, long_name, uuid));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _confirmRequest: function(applet, device_path, name, long_name, pin) {
 | 
			
		||||
        this._ensureSource();
 | 
			
		||||
        this._source.notify(new ConfirmNotification(this._source, this._applet, device_path, name, long_name, pin));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _pinRequest: function(applet, device_path, name, long_name, numeric) {
 | 
			
		||||
        this._ensureSource();
 | 
			
		||||
        this._source.notify(new PinNotification(this._source, this._applet, device_path, name, long_name, numeric));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _cancelRequest: function() {
 | 
			
		||||
        this._source.destroy();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AuthNotification = new Lang.Class({
 | 
			
		||||
    Name: 'AuthNotification',
 | 
			
		||||
    Extends: MessageTray.Notification,
 | 
			
		||||
 | 
			
		||||
    _init: function(source, applet, device_path, name, long_name) {
 | 
			
		||||
        this.parent(source,
 | 
			
		||||
                    _("Bluetooth"),
 | 
			
		||||
                    _("Authorization request from %s").format(name),
 | 
			
		||||
                    { customContent: true });
 | 
			
		||||
        this.setResident(true);
 | 
			
		||||
 | 
			
		||||
        this._applet = applet;
 | 
			
		||||
        this._devicePath = device_path;
 | 
			
		||||
        this.addBody(_("Device %s wants to pair with this computer").format(long_name));
 | 
			
		||||
 | 
			
		||||
        this.addButton('allow', _("Allow"));
 | 
			
		||||
        this.addButton('deny', _("Deny"));
 | 
			
		||||
 | 
			
		||||
        this.connect('action-invoked', Lang.bind(this, function(self, action) {
 | 
			
		||||
            if (action == 'allow')
 | 
			
		||||
                this._applet.agent_reply_confirm(this._devicePath, true);
 | 
			
		||||
            else
 | 
			
		||||
                this._applet.agent_reply_confirm(this._devicePath, false);
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const AuthServiceNotification = new Lang.Class({
 | 
			
		||||
    Name: 'AuthServiceNotification',
 | 
			
		||||
    Extends: MessageTray.Notification,
 | 
			
		||||
 | 
			
		||||
    _init: function(source, applet, device_path, name, long_name, uuid) {
 | 
			
		||||
        this.parent(source,
 | 
			
		||||
                    _("Bluetooth"),
 | 
			
		||||
                    _("Authorization request from %s").format(name),
 | 
			
		||||
                    { customContent: true });
 | 
			
		||||
        this.setResident(true);
 | 
			
		||||
 | 
			
		||||
        this._applet = applet;
 | 
			
		||||
        this._devicePath = device_path;
 | 
			
		||||
        this.addBody(_("Device %s wants access to the service '%s'").format(long_name, uuid));
 | 
			
		||||
 | 
			
		||||
        this.addButton('always-grant', _("Always grant access"));
 | 
			
		||||
        this.addButton('grant', _("Grant this time only"));
 | 
			
		||||
        this.addButton('reject', _("Reject"));
 | 
			
		||||
 | 
			
		||||
        this.connect('action-invoked', Lang.bind(this, function(self, action) {
 | 
			
		||||
            switch (action) {
 | 
			
		||||
            case 'always-grant':
 | 
			
		||||
                this._applet.agent_reply_auth_service(this._devicePath, true, true);
 | 
			
		||||
                break;
 | 
			
		||||
            case 'grant':
 | 
			
		||||
                this._applet.agent_reply_auth_service(this._devicePath, true, false);
 | 
			
		||||
                break;
 | 
			
		||||
            case 'reject':
 | 
			
		||||
            default:
 | 
			
		||||
                this._applet.agent_reply_auth_service(this._devicePath, false, false);
 | 
			
		||||
            }
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const ConfirmNotification = new Lang.Class({
 | 
			
		||||
    Name: 'ConfirmNotification',
 | 
			
		||||
    Extends: MessageTray.Notification,
 | 
			
		||||
 | 
			
		||||
    _init: function(source, applet, device_path, name, long_name, pin) {
 | 
			
		||||
        this.parent(source,
 | 
			
		||||
                    _("Bluetooth"),
 | 
			
		||||
                    /* Translators: argument is the device short name */
 | 
			
		||||
                    _("Pairing confirmation for %s").format(name),
 | 
			
		||||
                    { customContent: true });
 | 
			
		||||
        this.setResident(true);
 | 
			
		||||
 | 
			
		||||
        this._applet = applet;
 | 
			
		||||
        this._devicePath = device_path;
 | 
			
		||||
        this.addBody(_("Device %s wants to pair with this computer").format(long_name));
 | 
			
		||||
        this.addBody(_("Please confirm whether the Passkey '%06d' matches the one on the device.").format(pin));
 | 
			
		||||
 | 
			
		||||
        /* Translators: this is the verb, not the noun */
 | 
			
		||||
        this.addButton('matches', _("Matches"));
 | 
			
		||||
        this.addButton('does-not-match', _("Does not match"));
 | 
			
		||||
 | 
			
		||||
        this.connect('action-invoked', Lang.bind(this, function(self, action) {
 | 
			
		||||
            if (action == 'matches')
 | 
			
		||||
                this._applet.agent_reply_confirm(this._devicePath, true);
 | 
			
		||||
            else
 | 
			
		||||
                this._applet.agent_reply_confirm(this._devicePath, false);
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const PinNotification = new Lang.Class({
 | 
			
		||||
    Name: 'PinNotification',
 | 
			
		||||
    Extends: MessageTray.Notification,
 | 
			
		||||
 | 
			
		||||
    _init: function(source, applet, device_path, name, long_name, numeric) {
 | 
			
		||||
        this.parent(source,
 | 
			
		||||
                    _("Bluetooth"),
 | 
			
		||||
                    _("Pairing request for %s").format(name),
 | 
			
		||||
                    { customContent: true });
 | 
			
		||||
        this.setResident(true);
 | 
			
		||||
 | 
			
		||||
        this._applet = applet;
 | 
			
		||||
        this._devicePath = device_path;
 | 
			
		||||
        this._numeric = numeric;
 | 
			
		||||
        this.addBody(_("Device %s wants to pair with this computer").format(long_name));
 | 
			
		||||
        this.addBody(_("Please enter the PIN mentioned on the device."));
 | 
			
		||||
 | 
			
		||||
        this._entry = new St.Entry();
 | 
			
		||||
        this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) {
 | 
			
		||||
            let key = event.get_key_symbol();
 | 
			
		||||
            if (key == Clutter.KEY_Return) {
 | 
			
		||||
                if (this._canActivateOkButton())
 | 
			
		||||
                    this.emit('action-invoked', 'ok');
 | 
			
		||||
                return true;
 | 
			
		||||
            } else if (key == Clutter.KEY_Escape) {
 | 
			
		||||
                this.emit('action-invoked', 'cancel');
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
            return false;
 | 
			
		||||
        }));
 | 
			
		||||
        this.addActor(this._entry);
 | 
			
		||||
 | 
			
		||||
        this.addButton('ok', _("OK"));
 | 
			
		||||
        this.addButton('cancel', _("Cancel"));
 | 
			
		||||
 | 
			
		||||
        this.setButtonSensitive('ok', this._canActivateOkButton());
 | 
			
		||||
        this._entry.clutter_text.connect('text-changed', Lang.bind(this,
 | 
			
		||||
            function() {
 | 
			
		||||
                this.setButtonSensitive('ok', this._canActivateOkButton());
 | 
			
		||||
            }));
 | 
			
		||||
 | 
			
		||||
        this.connect('action-invoked', Lang.bind(this, function(self, action) {
 | 
			
		||||
            if (action == 'ok') {
 | 
			
		||||
                if (this._numeric) {
 | 
			
		||||
                    let num = parseInt(this._entry.text);
 | 
			
		||||
                    if (isNaN(num)) {
 | 
			
		||||
                        // user reply was empty, or was invalid
 | 
			
		||||
                        // cancel the operation
 | 
			
		||||
                        num = -1;
 | 
			
		||||
                    }
 | 
			
		||||
                    this._applet.agent_reply_passkey(this._devicePath, num);
 | 
			
		||||
                } else
 | 
			
		||||
                    this._applet.agent_reply_pincode(this._devicePath, this._entry.text);
 | 
			
		||||
            } else {
 | 
			
		||||
                if (this._numeric)
 | 
			
		||||
                    this._applet.agent_reply_passkey(this._devicePath, -1);
 | 
			
		||||
                else
 | 
			
		||||
                    this._applet.agent_reply_pincode(this._devicePath, null);
 | 
			
		||||
            }
 | 
			
		||||
            this.destroy();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _canActivateOkButton: function() {
 | 
			
		||||
        // PINs have a fixed length of 6
 | 
			
		||||
        if (this._numeric)
 | 
			
		||||
            return this._entry.clutter_text.text.length == 6;
 | 
			
		||||
        else
 | 
			
		||||
            return true;
 | 
			
		||||
    }
 | 
			
		||||
            this._item.status.text = _("Not Connected");
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -11,9 +11,11 @@ const Slider = imports.ui.slider;
 | 
			
		||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
 | 
			
		||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
 | 
			
		||||
 | 
			
		||||
const BrightnessInterface = <interface name="org.gnome.SettingsDaemon.Power.Screen">
 | 
			
		||||
<property name='Brightness' type='i' access='readwrite'/>
 | 
			
		||||
</interface>;
 | 
			
		||||
const BrightnessInterface = '<node> \
 | 
			
		||||
<interface name="org.gnome.SettingsDaemon.Power.Screen"> \
 | 
			
		||||
<property name="Brightness" type="i" access="readwrite"/> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const BrightnessProxy = Gio.DBusProxy.makeProxyWrapper(BrightnessInterface);
 | 
			
		||||
 | 
			
		||||
@@ -46,7 +48,7 @@ const Indicator = new Lang.Class({
 | 
			
		||||
        this._item.actor.add(icon);
 | 
			
		||||
        this._item.actor.add(this._slider.actor, { expand: true });
 | 
			
		||||
        this._item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) {
 | 
			
		||||
            this._slider.startDragging(event);
 | 
			
		||||
            return this._slider.startDragging(event);
 | 
			
		||||
        }));
 | 
			
		||||
        this._item.actor.connect('key-press-event', Lang.bind(this, function(actor, event) {
 | 
			
		||||
            return this._slider.onKeyPressEvent(actor, event);
 | 
			
		||||
 
 | 
			
		||||
@@ -41,11 +41,13 @@ const MAX_INPUT_SOURCE_ACTIVATION_TIME = 4000; // ms
 | 
			
		||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Keyboard';
 | 
			
		||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Keyboard';
 | 
			
		||||
 | 
			
		||||
const KeyboardManagerInterface = <interface name="org.gnome.SettingsDaemon.Keyboard">
 | 
			
		||||
<method name="SetInputSource">
 | 
			
		||||
    <arg type="u" direction="in" />
 | 
			
		||||
</method>
 | 
			
		||||
</interface>;
 | 
			
		||||
const KeyboardManagerInterface = '<node> \
 | 
			
		||||
<interface name="org.gnome.SettingsDaemon.Keyboard"> \
 | 
			
		||||
<method name="SetInputSource"> \
 | 
			
		||||
    <arg type="u" direction="in" /> \
 | 
			
		||||
</method> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const KeyboardManagerProxy = Gio.DBusProxy.makeProxyWrapper(KeyboardManagerInterface);
 | 
			
		||||
 | 
			
		||||
@@ -290,6 +292,10 @@ const InputSourcePopup = 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() {
 | 
			
		||||
@@ -339,7 +345,7 @@ const InputSourceIndicator = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' });
 | 
			
		||||
        this._hbox.add_child(this._container);
 | 
			
		||||
        this._hbox.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM));
 | 
			
		||||
        this._hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM));
 | 
			
		||||
 | 
			
		||||
        this.actor.add_child(this._hbox);
 | 
			
		||||
        this.actor.add_style_class_name('panel-status-button');
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										201
									
								
								js/ui/status/location.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								js/ui/status/location.js
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,201 @@
 | 
			
		||||
// -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*-
 | 
			
		||||
 | 
			
		||||
const GLib = imports.gi.GLib;
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
 | 
			
		||||
const LOCATION_SCHEMA = 'org.gnome.shell.location';
 | 
			
		||||
const MAX_ACCURACY_LEVEL = 'max-accuracy-level';
 | 
			
		||||
 | 
			
		||||
var GeoclueIface = '<node> \
 | 
			
		||||
  <interface name="org.freedesktop.GeoClue2.Manager"> \
 | 
			
		||||
    <property name="InUse" type="b" access="read"/> \
 | 
			
		||||
    <property name="AvailableAccuracyLevel" type="u" access="read"/> \
 | 
			
		||||
    <method name="AddAgent"> \
 | 
			
		||||
      <arg name="id" type="s" direction="in"/> \
 | 
			
		||||
    </method> \
 | 
			
		||||
  </interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const GeoclueManager = Gio.DBusProxy.makeProxyWrapper(GeoclueIface);
 | 
			
		||||
 | 
			
		||||
var AgentIface = '<node> \
 | 
			
		||||
  <interface name="org.freedesktop.GeoClue2.Agent"> \
 | 
			
		||||
    <property name="MaxAccuracyLevel" type="u" access="read"/> \
 | 
			
		||||
    <method name="AuthorizeApp"> \
 | 
			
		||||
      <arg name="desktop_id" type="s" direction="in"/> \
 | 
			
		||||
      <arg name="req_accuracy_level" type="u" direction="in"/> \
 | 
			
		||||
      <arg name="authorized" type="b" direction="out"/> \
 | 
			
		||||
      <arg name="allowed_accuracy_level" type="u" direction="out"/> \
 | 
			
		||||
    </method> \
 | 
			
		||||
  </interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const Indicator = new Lang.Class({
 | 
			
		||||
    Name: 'LocationIndicator',
 | 
			
		||||
    Extends: PanelMenu.SystemIndicator,
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent();
 | 
			
		||||
 | 
			
		||||
        this._settings = new Gio.Settings({ schema: LOCATION_SCHEMA });
 | 
			
		||||
        this._settings.connect('changed::' + MAX_ACCURACY_LEVEL,
 | 
			
		||||
                               Lang.bind(this, this._onMaxAccuracyLevelChanged));
 | 
			
		||||
 | 
			
		||||
        this._indicator = this._addIndicator();
 | 
			
		||||
        this._indicator.icon_name = 'find-location-symbolic';
 | 
			
		||||
 | 
			
		||||
        this._item = new PopupMenu.PopupSubMenuMenuItem(_("Location"), true);
 | 
			
		||||
        this._item.icon.icon_name = 'find-location-symbolic';
 | 
			
		||||
 | 
			
		||||
        this._agent = Gio.DBusExportedObject.wrapJSObject(AgentIface, this);
 | 
			
		||||
        this._agent.export(Gio.DBus.system, '/org/freedesktop/GeoClue2/Agent');
 | 
			
		||||
 | 
			
		||||
        this._item.status.text = _("Enabled");
 | 
			
		||||
        this._onOffAction = this._item.menu.addAction(_("Disable"), Lang.bind(this, this._onOnOffAction));
 | 
			
		||||
 | 
			
		||||
        this.menu.addMenuItem(this._item);
 | 
			
		||||
 | 
			
		||||
        this._watchId = Gio.bus_watch_name(Gio.BusType.SYSTEM,
 | 
			
		||||
                                           'org.freedesktop.GeoClue2',
 | 
			
		||||
                                           0,
 | 
			
		||||
                                           Lang.bind(this, this._connectToGeoclue),
 | 
			
		||||
                                           Lang.bind(this, this._onGeoclueVanished));
 | 
			
		||||
        Main.sessionMode.connect('updated', Lang.bind(this, this._onSessionUpdated));
 | 
			
		||||
        this._onSessionUpdated();
 | 
			
		||||
        this._onMaxAccuracyLevelChanged();
 | 
			
		||||
        this._connectToGeoclue();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get MaxAccuracyLevel() {
 | 
			
		||||
        return this._getMaxAccuracyLevel();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    // We (and geoclue) have currently no way to reliably identifying apps so
 | 
			
		||||
    // for now, lets just authorize all apps as long as they provide a valid
 | 
			
		||||
    // desktop ID. We also ensure they don't get more accuracy than global max.
 | 
			
		||||
    AuthorizeApp: function(desktop_id, reqAccuracyLevel) {
 | 
			
		||||
        var appSystem = Shell.AppSystem.get_default();
 | 
			
		||||
        var app = appSystem.lookup_app(desktop_id + ".desktop");
 | 
			
		||||
        if (app == null) {
 | 
			
		||||
            return [false, 0];
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        let allowedAccuracyLevel = clamp(reqAccuracyLevel, 0, this._getMaxAccuracyLevel());
 | 
			
		||||
        return [true, allowedAccuracyLevel];
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _syncIndicator: function() {
 | 
			
		||||
        if (this._proxy == null) {
 | 
			
		||||
            this._indicator.visible = false;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._indicator.visible = this._proxy.InUse;
 | 
			
		||||
        this._updateMenuLabels();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _connectToGeoclue: function() {
 | 
			
		||||
        if (this._proxy != null || this._connecting)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        this._connecting = true;
 | 
			
		||||
        new GeoclueManager(Gio.DBus.system,
 | 
			
		||||
                           'org.freedesktop.GeoClue2',
 | 
			
		||||
                           '/org/freedesktop/GeoClue2/Manager',
 | 
			
		||||
                           Lang.bind(this, this._onProxyReady));
 | 
			
		||||
        return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onProxyReady: function(proxy, error) {
 | 
			
		||||
        if (error != null) {
 | 
			
		||||
            log(error.message);
 | 
			
		||||
            this._connecting = false;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._proxy = proxy;
 | 
			
		||||
        this._propertiesChangedId = this._proxy.connect('g-properties-changed',
 | 
			
		||||
                                                        Lang.bind(this, this._onGeocluePropsChanged));
 | 
			
		||||
 | 
			
		||||
        this._availableAccuracyLevel = this._proxy.AvailableAccuracyLevel;
 | 
			
		||||
        this._syncIndicator();
 | 
			
		||||
 | 
			
		||||
        this._proxy.AddAgentRemote('gnome-shell', Lang.bind(this, this._onAgentRegistered));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onAgentRegistered: function(result, error) {
 | 
			
		||||
        this._connecting = false;
 | 
			
		||||
        this._notifyMaxAccuracyLevel();
 | 
			
		||||
 | 
			
		||||
        if (error != null)
 | 
			
		||||
            log(error.message);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onGeoclueVanished: function() {
 | 
			
		||||
        if (this._propertiesChangedId) {
 | 
			
		||||
            this._proxy.disconnect(this._propertiesChangedId);
 | 
			
		||||
            this._propertiesChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        this._proxy = null;
 | 
			
		||||
 | 
			
		||||
        this._syncIndicator();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onOnOffAction: function() {
 | 
			
		||||
        if (this._getMaxAccuracyLevel() == 0)
 | 
			
		||||
            this._settings.set_enum(MAX_ACCURACY_LEVEL, this._availableAccuracyLevel);
 | 
			
		||||
        else
 | 
			
		||||
            this._settings.set_enum(MAX_ACCURACY_LEVEL, 0);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSessionUpdated: function() {
 | 
			
		||||
        let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
 | 
			
		||||
        this.menu.setSensitive(sensitive);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateMenuLabels: function() {
 | 
			
		||||
        if (this._getMaxAccuracyLevel() == 0) {
 | 
			
		||||
            this._item.status.text = _("Disabled");
 | 
			
		||||
            this._onOffAction.label.text = _("Enable");
 | 
			
		||||
        } else {
 | 
			
		||||
            this._item.status.text = this._indicator.visible ? _("In Use") : _("Enabled");
 | 
			
		||||
            this._onOffAction.label.text = _("Disable");
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onMaxAccuracyLevelChanged: function() {
 | 
			
		||||
        this._updateMenuLabels();
 | 
			
		||||
 | 
			
		||||
        // Gotta ensure geoclue is up and we are registered as agent to it
 | 
			
		||||
        // before we emit the notify for this property change.
 | 
			
		||||
        if (!this._connectToGeoclue())
 | 
			
		||||
            this._notifyMaxAccuracyLevel();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getMaxAccuracyLevel: function() {
 | 
			
		||||
        return this._settings.get_enum(MAX_ACCURACY_LEVEL);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _notifyMaxAccuracyLevel: function() {
 | 
			
		||||
        let variant = new GLib.Variant('u', this._getMaxAccuracyLevel());
 | 
			
		||||
        this._agent.emit_property_changed('MaxAccuracyLevel', variant);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onGeocluePropsChanged: function(proxy, properties) {
 | 
			
		||||
        let unpacked = properties.deep_unpack();
 | 
			
		||||
        if ("InUse" in unpacked)
 | 
			
		||||
            this._syncIndicator();
 | 
			
		||||
        if ("AvailableAccuracyLevel" in unpacked)
 | 
			
		||||
            this._availableAccuracyLevel = this._proxy.AvailableAccuracyLevel;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
function clamp(value, min, max) {
 | 
			
		||||
    return Math.max(min, Math.min(max, value));
 | 
			
		||||
}
 | 
			
		||||
@@ -9,19 +9,22 @@ const NetworkManager = imports.gi.NetworkManager;
 | 
			
		||||
const NMClient = imports.gi.NMClient;
 | 
			
		||||
const NMGtk = imports.gi.NMGtk;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
const Shell = imports.gi.Shell;
 | 
			
		||||
const St = imports.gi.St;
 | 
			
		||||
 | 
			
		||||
const Hash = imports.misc.hash;
 | 
			
		||||
const Animation = imports.ui.animation;
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
const MessageTray = imports.ui.messageTray;
 | 
			
		||||
const ModalDialog = imports.ui.modalDialog;
 | 
			
		||||
const ModemManager = imports.misc.modemManager;
 | 
			
		||||
const Rfkill = imports.ui.status.rfkill;
 | 
			
		||||
const Util = imports.misc.util;
 | 
			
		||||
 | 
			
		||||
const NMConnectionCategory = {
 | 
			
		||||
    INVALID: 'invalid',
 | 
			
		||||
    WIRED: 'wired',
 | 
			
		||||
    WIRELESS: 'wireless',
 | 
			
		||||
    WWAN: 'wwan',
 | 
			
		||||
    VPN: 'vpn'
 | 
			
		||||
@@ -102,18 +105,34 @@ const NMConnectionItem = new Lang.Class({
 | 
			
		||||
        this._activeConnection = null;
 | 
			
		||||
        this._activeConnectionChangedId = 0;
 | 
			
		||||
 | 
			
		||||
        this._buildUI();
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildUI: function() {
 | 
			
		||||
        this.labelItem = new PopupMenu.PopupMenuItem('');
 | 
			
		||||
        this.labelItem.connect('activate', Lang.bind(this, this._toggle));
 | 
			
		||||
 | 
			
		||||
        this.switchItem = new PopupMenu.PopupSwitchMenuItem(connection.get_id(), false);
 | 
			
		||||
        this.switchItem.connect('toggled', Lang.bind(this, this._toggle));
 | 
			
		||||
 | 
			
		||||
        this._sync();
 | 
			
		||||
        this.radioItem = new PopupMenu.PopupMenuItem(this._connection.get_id(), false);
 | 
			
		||||
        this.radioItem.connect('activate', Lang.bind(this, this._activate));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        this.labelItem.destroy();
 | 
			
		||||
        this.switchItem.destroy();
 | 
			
		||||
        this.radioItem.destroy();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    updateForConnection: function(connection) {
 | 
			
		||||
        // connection should always be the same object
 | 
			
		||||
        // (and object path) as this._connection, but
 | 
			
		||||
        // this can be false if NetworkManager was restarted
 | 
			
		||||
        // and picked up connections in a different order
 | 
			
		||||
        // Just to be safe, we set it here again
 | 
			
		||||
 | 
			
		||||
        this._connection = connection;
 | 
			
		||||
        this.radioItem.label.text = connection.get_id();
 | 
			
		||||
        this._sync();
 | 
			
		||||
        this.emit('name-changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getName: function() {
 | 
			
		||||
@@ -129,9 +148,8 @@ const NMConnectionItem = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let isActive = this.isActive();
 | 
			
		||||
        this.labelItem.label.text = isActive ? _("Turn Off") : _("Connect");
 | 
			
		||||
        this.switchItem.setToggleState(isActive);
 | 
			
		||||
        this.switchItem.setStatus(this._getStatus());
 | 
			
		||||
        this.labelItem.label.text = isActive ? _("Turn Off") : this._section.getConnectLabel();
 | 
			
		||||
        this.radioItem.setOrnament(isActive ? PopupMenu.Ornament.DOT : PopupMenu.Ornament.NONE);
 | 
			
		||||
        this.emit('icon-changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -144,8 +162,11 @@ const NMConnectionItem = new Lang.Class({
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getStatus: function() {
 | 
			
		||||
        return null;
 | 
			
		||||
    _activate: function() {
 | 
			
		||||
        if (this._activeConnection == null)
 | 
			
		||||
            this._section.activateConnection(this._connection);
 | 
			
		||||
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _connectionStateChanged: function(ac, newstate, reason) {
 | 
			
		||||
@@ -176,15 +197,15 @@ const NMConnectionSection = new Lang.Class({
 | 
			
		||||
    _init: function(client) {
 | 
			
		||||
        this._client = client;
 | 
			
		||||
 | 
			
		||||
        this._connectionItems = new Hash.Map();
 | 
			
		||||
        this._connectionItems = new Map();
 | 
			
		||||
        this._connections = [];
 | 
			
		||||
 | 
			
		||||
        this._labelSection = new PopupMenu.PopupMenuSection();
 | 
			
		||||
        this._switchSection = new PopupMenu.PopupMenuSection();
 | 
			
		||||
        this._radioSection = new PopupMenu.PopupMenuSection();
 | 
			
		||||
 | 
			
		||||
        this.item = new PopupMenu.PopupSubMenuMenuItem('', true);
 | 
			
		||||
        this.item.menu.addMenuItem(this._labelSection);
 | 
			
		||||
        this.item.menu.addMenuItem(this._switchSection);
 | 
			
		||||
        this.item.menu.addMenuItem(this._radioSection);
 | 
			
		||||
 | 
			
		||||
        this.connect('icon-changed', Lang.bind(this, this._sync));
 | 
			
		||||
    },
 | 
			
		||||
@@ -194,9 +215,9 @@ const NMConnectionSection = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let nItems = this._connectionItems.size();
 | 
			
		||||
        let nItems = this._connectionItems.size;
 | 
			
		||||
 | 
			
		||||
        this._switchSection.actor.visible = (nItems > 1);
 | 
			
		||||
        this._radioSection.actor.visible = (nItems > 1);
 | 
			
		||||
        this._labelSection.actor.visible = (nItems == 1);
 | 
			
		||||
 | 
			
		||||
        this.item.status.text = this._getStatus();
 | 
			
		||||
@@ -211,19 +232,12 @@ const NMConnectionSection = new Lang.Class({
 | 
			
		||||
            this.item.label.text = '';
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getStatus: function() {
 | 
			
		||||
        let values = this._connectionItems.values();
 | 
			
		||||
        for (let i = 0; i < values.length; i++) {
 | 
			
		||||
            let item = values[i];
 | 
			
		||||
            if (item.isActive())
 | 
			
		||||
                return item.getName();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return _("Off");
 | 
			
		||||
    _getMenuIcon: function() {
 | 
			
		||||
        return this.getIndicatorIcon();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hasConnection: function(connection) {
 | 
			
		||||
        return this._connectionItems.has(connection.get_uuid());
 | 
			
		||||
    getConnectLabel: function() {
 | 
			
		||||
        return _("Connect");
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _connectionValid: function(connection) {
 | 
			
		||||
@@ -231,10 +245,7 @@ const NMConnectionSection = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _connectionSortFunction: function(one, two) {
 | 
			
		||||
        if (one._timestamp == two._timestamp)
 | 
			
		||||
            return GLib.utf8_collate(one.get_id(), two.get_id());
 | 
			
		||||
 | 
			
		||||
        return two._timestamp - one._timestamp;
 | 
			
		||||
        return GLib.utf8_collate(one.get_id(), two.get_id());
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _makeConnectionItem: function(connection) {
 | 
			
		||||
@@ -245,10 +256,20 @@ const NMConnectionSection = new Lang.Class({
 | 
			
		||||
        if (!this._connectionValid(connection))
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (this._hasConnection(connection))
 | 
			
		||||
            return;
 | 
			
		||||
        // This function is called everytime connection is added or updated
 | 
			
		||||
        // In the usual case, we already added this connection and UUID
 | 
			
		||||
        // didn't change. So we need to check if we already have an item,
 | 
			
		||||
        // and update it for properties in the connection that changed
 | 
			
		||||
        // (the only one we care about is the name)
 | 
			
		||||
        // But it's also possible we didn't know about this connection
 | 
			
		||||
        // (eg, during coldplug, or because it was updated and suddenly
 | 
			
		||||
        // it's valid for this device), in which case we add a new item
 | 
			
		||||
 | 
			
		||||
        this._addConnection(connection);
 | 
			
		||||
        let item = this._connectionItems.get(connection.get_uuid());
 | 
			
		||||
        if (item)
 | 
			
		||||
            item.updateForConnection(connection);
 | 
			
		||||
        else
 | 
			
		||||
            this._addConnection(connection);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _addConnection: function(connection) {
 | 
			
		||||
@@ -262,17 +283,23 @@ const NMConnectionSection = new Lang.Class({
 | 
			
		||||
        item.connect('activation-failed', Lang.bind(this, function(item, reason) {
 | 
			
		||||
            this.emit('activation-failed', reason);
 | 
			
		||||
        }));
 | 
			
		||||
        item.connect('name-changed', Lang.bind(this, this._sync));
 | 
			
		||||
 | 
			
		||||
        let pos = Util.insertSorted(this._connections, connection, Lang.bind(this, this._connectionSortFunction));
 | 
			
		||||
        this._labelSection.addMenuItem(item.labelItem, pos);
 | 
			
		||||
        this._switchSection.addMenuItem(item.switchItem, pos);
 | 
			
		||||
        this._radioSection.addMenuItem(item.radioItem, pos);
 | 
			
		||||
        this._connectionItems.set(connection.get_uuid(), item);
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    removeConnection: function(connection) {
 | 
			
		||||
        this._connectionItems.get(connection.get_uuid()).destroy();
 | 
			
		||||
        this._connectionItems.delete(connection.get_uuid());
 | 
			
		||||
        let uuid = connection.get_uuid();
 | 
			
		||||
        let item = this._connectionItems.get(uuid);
 | 
			
		||||
        if (item == undefined)
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        item.destroy();
 | 
			
		||||
        this._connectionItems.delete(uuid);
 | 
			
		||||
 | 
			
		||||
        let pos = this._connections.indexOf(connection);
 | 
			
		||||
        this._connections.splice(pos, 1);
 | 
			
		||||
@@ -293,11 +320,24 @@ const NMConnectionDevice = new Lang.Class({
 | 
			
		||||
        this._settings = settings;
 | 
			
		||||
 | 
			
		||||
        this._autoConnectItem = this.item.menu.addAction(_("Connect"), Lang.bind(this, this._autoConnect));
 | 
			
		||||
        this._deactivateItem = this._radioSection.addAction(_("Turn Off"), Lang.bind(this, this.deactivateConnection));
 | 
			
		||||
 | 
			
		||||
        this._stateChangedId = this._device.connect('state-changed', Lang.bind(this, this._deviceStateChanged));
 | 
			
		||||
        this._activeConnectionChangedId = this._device.connect('notify::active-connection', Lang.bind(this, this._activeConnectionChanged));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _canReachInternet: function() {
 | 
			
		||||
        if (this._client.primary_connection != this._device.active_connection)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        return this._client.connectivity == NetworkManager.ConnectivityState.FULL;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _autoConnect: function() {
 | 
			
		||||
        let connection = new NetworkManager.Connection();
 | 
			
		||||
        this._client.add_and_activate_connection(connection, this._device, null, null);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
        if (this._stateChangedId) {
 | 
			
		||||
            GObject.Object.prototype.disconnect.call(this._device, this._stateChangedId);
 | 
			
		||||
@@ -305,7 +345,7 @@ const NMConnectionDevice = new Lang.Class({
 | 
			
		||||
        }
 | 
			
		||||
        if (this._activeConnectionChangedId) {
 | 
			
		||||
            GObject.Object.prototype.disconnect.call(this._device, this._activeConnectionChangedId);
 | 
			
		||||
            this._stateChangedId = 0;
 | 
			
		||||
            this._activeConnectionChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.parent();
 | 
			
		||||
@@ -365,8 +405,9 @@ const NMConnectionDevice = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let nItems = this._connectionItems.size();
 | 
			
		||||
        let nItems = this._connectionItems.size;
 | 
			
		||||
        this._autoConnectItem.actor.visible = (nItems == 0);
 | 
			
		||||
        this._deactivateItem.actor.visible = this._device.state > NetworkManager.DeviceState.DISCONNECTED;
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
@@ -378,7 +419,7 @@ const NMConnectionDevice = new Lang.Class({
 | 
			
		||||
        case NetworkManager.DeviceState.DISCONNECTED:
 | 
			
		||||
            return _("Off");
 | 
			
		||||
        case NetworkManager.DeviceState.ACTIVATED:
 | 
			
		||||
            return this.parent();
 | 
			
		||||
            return _("Connected");
 | 
			
		||||
        case NetworkManager.DeviceState.UNMANAGED:
 | 
			
		||||
            /* Translators: this is for network devices that are physically present but are not
 | 
			
		||||
               under NetworkManager's control (and thus cannot be used in the menu) */
 | 
			
		||||
@@ -415,6 +456,48 @@ const NMConnectionDevice = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const NMDeviceWired = new Lang.Class({
 | 
			
		||||
    Name: 'NMDeviceWired',
 | 
			
		||||
    Extends: NMConnectionDevice,
 | 
			
		||||
    category: NMConnectionCategory.WIRED,
 | 
			
		||||
 | 
			
		||||
    _init: function(client, device, settings) {
 | 
			
		||||
        this.parent(client, device, settings);
 | 
			
		||||
 | 
			
		||||
        this.item.menu.addMenuItem(createSettingsAction(_("Wired Settings"), device));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _hasCarrier: function() {
 | 
			
		||||
        if (this._device instanceof NMClient.DeviceEthernet)
 | 
			
		||||
            return this._device.carrier;
 | 
			
		||||
        else
 | 
			
		||||
            return true;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        this.item.actor.visible = this._hasCarrier();
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getIndicatorIcon: function() {
 | 
			
		||||
        if (this._device.active_connection) {
 | 
			
		||||
            let state = this._device.active_connection.state;
 | 
			
		||||
 | 
			
		||||
            if (state == NetworkManager.ActiveConnectionState.ACTIVATING) {
 | 
			
		||||
                return 'network-wired-acquiring-symbolic';
 | 
			
		||||
            } else if (state == NetworkManager.ActiveConnectionState.ACTIVATED) {
 | 
			
		||||
                if (this._canReachInternet())
 | 
			
		||||
                    return 'network-wired-symbolic';
 | 
			
		||||
                else
 | 
			
		||||
                    return 'network-wired-no-route-symbolic';
 | 
			
		||||
            } else {
 | 
			
		||||
                return 'network-wired-disconnected-symbolic';
 | 
			
		||||
            }
 | 
			
		||||
        } else
 | 
			
		||||
            return 'network-wired-disconnected-symbolic';
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const NMDeviceModem = new Lang.Class({
 | 
			
		||||
    Name: 'NMDeviceModem',
 | 
			
		||||
    Extends: NMConnectionDevice,
 | 
			
		||||
@@ -477,28 +560,20 @@ const NMDeviceModem = new Lang.Class({
 | 
			
		||||
            return this.parent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getMenuIcon: function() {
 | 
			
		||||
        if (this._device.active_connection)
 | 
			
		||||
            return this.getIndicatorIcon();
 | 
			
		||||
        else
 | 
			
		||||
    getIndicatorIcon: function() {
 | 
			
		||||
        if (this._device.active_connection) {
 | 
			
		||||
            if (this._device.active_connection.state == NetworkManager.ActiveConnectionState.ACTIVATING)
 | 
			
		||||
                return 'network-cellular-acquiring-symbolic';
 | 
			
		||||
 | 
			
		||||
            return this._getSignalIcon();
 | 
			
		||||
        } else {
 | 
			
		||||
            return 'network-cellular-signal-none-symbolic';
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getSignalIcon: function() {
 | 
			
		||||
        return 'network-cellular-signal-' + signalToIcon(this._mobileDevice.signal_quality) + '-symbolic';
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getIndicatorIcon: function() {
 | 
			
		||||
        if (this._device.active_connection.state == NetworkManager.ActiveConnectionState.ACTIVATING)
 | 
			
		||||
            return 'network-cellular-acquiring-symbolic';
 | 
			
		||||
 | 
			
		||||
        if (!this._mobileDevice) {
 | 
			
		||||
            // this can happen for bluetooth in PAN mode
 | 
			
		||||
            return 'network-cellular-connected-symbolic';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return this._getSignalIcon();
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
const NMDeviceBluetooth = new Lang.Class({
 | 
			
		||||
@@ -512,33 +587,26 @@ const NMDeviceBluetooth = new Lang.Class({
 | 
			
		||||
        this.item.menu.addMenuItem(createSettingsAction(_("Mobile Broadband Settings"), device));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _autoConnect: function() {
 | 
			
		||||
        // FIXME: DUN devices are configured like modems, so
 | 
			
		||||
        // We need to spawn the mobile wizard
 | 
			
		||||
        // but the network panel doesn't support bluetooth at the moment
 | 
			
		||||
        // so we just create an empty connection and hope
 | 
			
		||||
        // that this phone supports PAN
 | 
			
		||||
 | 
			
		||||
        let connection = new NetworkManager.Connection();
 | 
			
		||||
        this._client.add_and_activate_connection(connection, this._device, null, null);
 | 
			
		||||
        return true;
 | 
			
		||||
    _getDescription: function() {
 | 
			
		||||
        return this._device.name;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getMenuIcon: function() {
 | 
			
		||||
        if (this._device.active_connection)
 | 
			
		||||
            return this.getIndicatorIcon();
 | 
			
		||||
        else
 | 
			
		||||
            return 'network-cellular-signal-none-symbolic';
 | 
			
		||||
    getConnectLabel: function() {
 | 
			
		||||
        return _("Use as Internet connection");
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getIndicatorIcon: function() {
 | 
			
		||||
        let state = this._device.active_connection.state;
 | 
			
		||||
        if (state == NetworkManager.ActiveConnectionState.ACTIVATING)
 | 
			
		||||
            return 'network-cellular-acquiring-symbolic';
 | 
			
		||||
        else if (state == NetworkManager.ActiveConnectionState.ACTIVATED)
 | 
			
		||||
            return 'network-cellular-connected-symbolic';
 | 
			
		||||
        else
 | 
			
		||||
        if (this._device.active_connection) {
 | 
			
		||||
            let state = this._device.active_connection.state;
 | 
			
		||||
            if (state == NetworkManager.ActiveConnectionState.ACTIVATING)
 | 
			
		||||
                return 'network-cellular-acquiring-symbolic';
 | 
			
		||||
            else if (state == NetworkManager.ActiveConnectionState.ACTIVATED)
 | 
			
		||||
                return 'network-cellular-connected-symbolic';
 | 
			
		||||
            else
 | 
			
		||||
                return 'network-cellular-signal-none-symbolic';
 | 
			
		||||
        } else {
 | 
			
		||||
            return 'network-cellular-signal-none-symbolic';
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -549,33 +617,30 @@ const NMWirelessDialogItem = new Lang.Class({
 | 
			
		||||
        this._network = network;
 | 
			
		||||
        this._ap = network.accessPoints[0];
 | 
			
		||||
 | 
			
		||||
        this.actor = new St.Button({ style_class: 'nm-dialog-item',
 | 
			
		||||
                                     can_focus: true,
 | 
			
		||||
                                     x_fill: true });
 | 
			
		||||
        this.actor = new St.BoxLayout({ style_class: 'nm-dialog-item',
 | 
			
		||||
                                        can_focus: true,
 | 
			
		||||
                                        reactive: true });
 | 
			
		||||
        this.actor.connect('key-focus-in', Lang.bind(this, function() {
 | 
			
		||||
            this.emit('selected');
 | 
			
		||||
        }));
 | 
			
		||||
        this.actor.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
        let action = new Clutter.ClickAction();
 | 
			
		||||
        action.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
            this.actor.grab_key_focus();
 | 
			
		||||
        }));
 | 
			
		||||
 | 
			
		||||
        this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent));
 | 
			
		||||
 | 
			
		||||
        this._content = new St.BoxLayout({ style_class: 'nm-dialog-item-box' });
 | 
			
		||||
        this.actor.set_child(this._content);
 | 
			
		||||
        this.actor.add_action(action);
 | 
			
		||||
 | 
			
		||||
        let title = ssidToLabel(this._ap.get_ssid());
 | 
			
		||||
        this._label = new St.Label({ text: title });
 | 
			
		||||
 | 
			
		||||
        this.actor.label_actor = this._label;
 | 
			
		||||
        this._content.add(this._label, { x_align: St.Align.START });
 | 
			
		||||
        this.actor.add(this._label, { x_align: St.Align.START });
 | 
			
		||||
 | 
			
		||||
        this._selectedIcon = new St.Icon({ style_class: 'nm-dialog-icon',
 | 
			
		||||
                                           icon_name: 'object-select-symbolic' });
 | 
			
		||||
        this._content.add(this._selectedIcon);
 | 
			
		||||
        this.actor.add(this._selectedIcon);
 | 
			
		||||
 | 
			
		||||
        this._icons = new St.BoxLayout({ style_class: 'nm-dialog-icons' });
 | 
			
		||||
        this._content.add(this._icons, { expand: true, x_fill: false, x_align: St.Align.END });
 | 
			
		||||
        this.actor.add(this._icons, { expand: true, x_fill: false, x_align: St.Align.END });
 | 
			
		||||
 | 
			
		||||
        this._secureIcon = new St.Icon({ style_class: 'nm-dialog-icon' });
 | 
			
		||||
        if (this._ap._secType != NMAccessPointSecurity.NONE)
 | 
			
		||||
@@ -606,16 +671,6 @@ const NMWirelessDialogItem = new Lang.Class({
 | 
			
		||||
            return 'network-workgroup-symbolic';
 | 
			
		||||
        else
 | 
			
		||||
            return 'network-wireless-signal-' + signalToIcon(this._ap.strength) + '-symbolic';
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onKeyPressEvent: function(actor, event) {
 | 
			
		||||
        let symbol = event.get_key_symbol();
 | 
			
		||||
        if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return
 | 
			
		||||
            || symbol == Clutter.KEY_KP_Enter) {
 | 
			
		||||
            this.emit('connect');
 | 
			
		||||
            return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(NMWirelessDialogItem.prototype);
 | 
			
		||||
@@ -630,6 +685,13 @@ const NMWirelessDialog = new Lang.Class({
 | 
			
		||||
        this._client = client;
 | 
			
		||||
        this._device = device;
 | 
			
		||||
 | 
			
		||||
        this._wirelessEnabledChangedId = this._client.connect('notify::wireless-enabled',
 | 
			
		||||
                                                              Lang.bind(this, this._syncView));
 | 
			
		||||
 | 
			
		||||
        this._rfkill = Rfkill.getRfkillManager();
 | 
			
		||||
        this._airplaneModeChangedId = this._rfkill.connect('airplane-mode-changed',
 | 
			
		||||
                                                           Lang.bind(this, this._syncView));
 | 
			
		||||
 | 
			
		||||
        this._networks = [];
 | 
			
		||||
        this._buildLayout();
 | 
			
		||||
 | 
			
		||||
@@ -651,6 +713,12 @@ const NMWirelessDialog = new Lang.Class({
 | 
			
		||||
        this._selectedNetwork = null;
 | 
			
		||||
        this._activeApChanged();
 | 
			
		||||
        this._updateSensitivity();
 | 
			
		||||
        this._syncView();
 | 
			
		||||
 | 
			
		||||
        if (accessPoints.length == 0) {
 | 
			
		||||
            /* If there are no visible access points, request a scan */
 | 
			
		||||
            this._device.request_scan_simple(null);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    destroy: function() {
 | 
			
		||||
@@ -666,6 +734,14 @@ const NMWirelessDialog = new Lang.Class({
 | 
			
		||||
            GObject.Object.prototype.disconnect.call(this._device, this._activeApChangedId);
 | 
			
		||||
            this._activeApChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._wirelessEnabledChangedId) {
 | 
			
		||||
            this._client.disconnect(this._wirelessEnabledChangedId);
 | 
			
		||||
            this._wirelessEnabledChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._airplaneModeChangedId) {
 | 
			
		||||
            this._rfkill.disconnect(this._airplaneModeChangedId);
 | 
			
		||||
            this._airplaneModeChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
@@ -687,13 +763,44 @@ const NMWirelessDialog = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateSensitivity: function() {
 | 
			
		||||
        let connectSensitive = this._selectedNetwork && (this._selectedNetwork != this._activeNetwork);
 | 
			
		||||
        let connectSensitive = this._client.wireless_enabled && this._selectedNetwork && (this._selectedNetwork != this._activeNetwork);
 | 
			
		||||
        this._connectButton.reactive = connectSensitive;
 | 
			
		||||
        this._connectButton.can_focus = connectSensitive;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _updateVisibility: function() {
 | 
			
		||||
        this._noNetworksLabel.visible = (this._networks.length == 0);
 | 
			
		||||
    _syncView: function() {
 | 
			
		||||
        if (this._rfkill.airplaneMode) {
 | 
			
		||||
            this._airplaneBox.show();
 | 
			
		||||
 | 
			
		||||
            this._airplaneIcon.icon_name = 'airplane-mode-symbolic';
 | 
			
		||||
            this._airplaneHeadline.text = _("Airplane Mode is On");
 | 
			
		||||
            this._airplaneText.text = _("Wi-Fi is disabled when airplane mode is on.");
 | 
			
		||||
            this._airplaneButton.label = _("Turn Off Airplane Mode");
 | 
			
		||||
 | 
			
		||||
            this._airplaneButton.visible = !this._rfkill.hwAirplaneMode;
 | 
			
		||||
            this._airplaneInactive.visible = this._rfkill.hwAirplaneMode;
 | 
			
		||||
            this._noNetworksBox.hide();
 | 
			
		||||
        } else if (!this._client.wireless_enabled) {
 | 
			
		||||
            this._airplaneBox.show();
 | 
			
		||||
 | 
			
		||||
            this._airplaneIcon.icon_name = 'dialog-information-symbolic';
 | 
			
		||||
            this._airplaneHeadline.text = _("Wi-Fi is Off");
 | 
			
		||||
            this._airplaneText.text = _("Wi-Fi needs to be turned on in order to connect to a network.");
 | 
			
		||||
            this._airplaneButton.label = _("Turn On Wi-Fi");
 | 
			
		||||
 | 
			
		||||
            this._airplaneButton.show();
 | 
			
		||||
            this._airplaneInactive.hide();
 | 
			
		||||
            this._noNetworksBox.hide();
 | 
			
		||||
        } else {
 | 
			
		||||
            this._airplaneBox.hide();
 | 
			
		||||
 | 
			
		||||
            this._noNetworksBox.visible = (this._networks.length == 0);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (this._noNetworksBox.visible)
 | 
			
		||||
            this._noNetworksSpinner.play();
 | 
			
		||||
        else
 | 
			
		||||
            this._noNetworksSpinner.stop();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildLayout: function() {
 | 
			
		||||
@@ -727,11 +834,43 @@ const NMWirelessDialog = new Lang.Class({
 | 
			
		||||
        this._scrollView.add_actor(this._itemBox);
 | 
			
		||||
        this._stack.add_child(this._scrollView);
 | 
			
		||||
 | 
			
		||||
        this._noNetworksLabel = new St.Label({ style_class: 'no-networks-label',
 | 
			
		||||
        this._noNetworksBox = new St.BoxLayout({ vertical: true,
 | 
			
		||||
                                                 style_class: 'no-networks-box',
 | 
			
		||||
                                                 x_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                                 y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
 | 
			
		||||
        this._noNetworksSpinner = new Animation.AnimatedIcon(global.datadir + '/theme/process-working.svg', 24, 24);
 | 
			
		||||
        this._noNetworksBox.add_actor(this._noNetworksSpinner.actor);
 | 
			
		||||
        this._noNetworksBox.add_actor(new St.Label({ style_class: 'no-networks-label',
 | 
			
		||||
                                                     text: _("No Networks") }));
 | 
			
		||||
        this._stack.add_child(this._noNetworksBox);
 | 
			
		||||
 | 
			
		||||
        this._airplaneBox = new St.BoxLayout({ vertical: true,
 | 
			
		||||
                                               style_class: 'nm-dialog-airplane-box',
 | 
			
		||||
                                               x_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                               y_align: Clutter.ActorAlign.CENTER,
 | 
			
		||||
                                               text: _("No Networks") });
 | 
			
		||||
        this._stack.add_child(this._noNetworksLabel);
 | 
			
		||||
                                               y_align: Clutter.ActorAlign.CENTER });
 | 
			
		||||
        this._airplaneIcon = new St.Icon({ icon_size: 48 });
 | 
			
		||||
        this._airplaneHeadline = new St.Label({ style_class: 'nm-dialog-airplane-headline' });
 | 
			
		||||
        this._airplaneText = new St.Label({ style_class: 'nm-dialog-airplane-text' });
 | 
			
		||||
 | 
			
		||||
        let airplaneSubStack = new St.Widget({ layout_manager: new Clutter.BinLayout });
 | 
			
		||||
        this._airplaneButton = new St.Button({ style_class: 'modal-dialog-button' });
 | 
			
		||||
        this._airplaneButton.connect('clicked', Lang.bind(this, function() {
 | 
			
		||||
            if (this._rfkill.airplaneMode)
 | 
			
		||||
                this._rfkill.airplaneMode = false;
 | 
			
		||||
            else
 | 
			
		||||
                this._client.wireless_enabled = true;
 | 
			
		||||
        }));
 | 
			
		||||
        airplaneSubStack.add_actor(this._airplaneButton);
 | 
			
		||||
        this._airplaneInactive = new St.Label({ style_class: 'nm-dialog-airplane-text',
 | 
			
		||||
                                                text: _("Use hardware switch to turn off") });
 | 
			
		||||
        airplaneSubStack.add_actor(this._airplaneInactive);
 | 
			
		||||
 | 
			
		||||
        this._airplaneBox.add(this._airplaneIcon, { x_align: St.Align.MIDDLE });
 | 
			
		||||
        this._airplaneBox.add(this._airplaneHeadline, { x_align: St.Align.MIDDLE });
 | 
			
		||||
        this._airplaneBox.add(this._airplaneText, { x_align: St.Align.MIDDLE });
 | 
			
		||||
        this._airplaneBox.add(airplaneSubStack, { x_align: St.Align.MIDDLE });
 | 
			
		||||
        this._stack.add_child(this._airplaneBox);
 | 
			
		||||
 | 
			
		||||
        this.contentLayout.add(this._stack, { expand: true });
 | 
			
		||||
 | 
			
		||||
@@ -748,16 +887,11 @@ const NMWirelessDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _connect: function() {
 | 
			
		||||
        let network = this._selectedNetwork;
 | 
			
		||||
        let accessPoints = network.accessPoints;
 | 
			
		||||
        if (network.connections.length > 0) {
 | 
			
		||||
            let connection = network.connections[0];
 | 
			
		||||
            for (let i = 0; i < accessPoints.length; i++) {
 | 
			
		||||
                if (accessPoints[i].connection_valid(connection)) {
 | 
			
		||||
                    this._client.activate_connection(connection, this._device, accessPoints[i].dbus_path, null);
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            this._client.activate_connection(connection, this._device, null, null);
 | 
			
		||||
        } else {
 | 
			
		||||
            let accessPoints = network.accessPoints;
 | 
			
		||||
            if ((accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT)
 | 
			
		||||
                || (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) {
 | 
			
		||||
                // 802.1x-enabled APs require further configuration, so they're
 | 
			
		||||
@@ -929,7 +1063,7 @@ const NMWirelessDialog = new Lang.Class({
 | 
			
		||||
            this._itemBox.insert_child_at_index(network.item.actor, newPos);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._updateVisibility();
 | 
			
		||||
        this._syncView();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _accessPointRemoved: function(device, accessPoint) {
 | 
			
		||||
@@ -944,14 +1078,14 @@ const NMWirelessDialog = new Lang.Class({
 | 
			
		||||
        network.accessPoints.splice(res.ap, 1);
 | 
			
		||||
 | 
			
		||||
        if (network.accessPoints.length == 0) {
 | 
			
		||||
            network.item.destroy();
 | 
			
		||||
            network.item.actor.destroy();
 | 
			
		||||
            this._networks.splice(res.network, 1);
 | 
			
		||||
        } else {
 | 
			
		||||
            network.item.updateBestAP(network.accessPoints[0]);
 | 
			
		||||
            this._resortItems();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._updateVisibility();
 | 
			
		||||
        this._syncView();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _resortItems: function() {
 | 
			
		||||
@@ -968,13 +1102,13 @@ const NMWirelessDialog = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _selectNetwork: function(network) {
 | 
			
		||||
        if (this._selectedNetwork)
 | 
			
		||||
            this._selectedNetwork.item.actor.checked = false;
 | 
			
		||||
            this._selectedNetwork.item.actor.remove_style_pseudo_class('selected');
 | 
			
		||||
 | 
			
		||||
        this._selectedNetwork = network;
 | 
			
		||||
        this._updateSensitivity();
 | 
			
		||||
 | 
			
		||||
        if (this._selectedNetwork)
 | 
			
		||||
            this._selectedNetwork.item.actor.checked = true;
 | 
			
		||||
            this._selectedNetwork.item.actor.add_style_pseudo_class('selected');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createNetworkItem: function(network) {
 | 
			
		||||
@@ -984,9 +1118,6 @@ const NMWirelessDialog = new Lang.Class({
 | 
			
		||||
            Util.ensureActorVisibleInScrollView(this._scrollView, network.item.actor);
 | 
			
		||||
            this._selectNetwork(network);
 | 
			
		||||
        }));
 | 
			
		||||
        network.item.connect('connect', Lang.bind(this, function() {
 | 
			
		||||
            this._connect();
 | 
			
		||||
        }));
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
@@ -1039,6 +1170,10 @@ const NMDeviceWireless = new Lang.Class({
 | 
			
		||||
            this._client.disconnect(this._wirelessHwEnabledChangedId);
 | 
			
		||||
            this._wirelessHwEnabledChangedId = 0;
 | 
			
		||||
        }
 | 
			
		||||
        if (this._dialog) {
 | 
			
		||||
            this._dialog.destroy();
 | 
			
		||||
            this._dialog = null;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this.item.destroy();
 | 
			
		||||
    },
 | 
			
		||||
@@ -1112,7 +1247,12 @@ const NMDeviceWireless = new Lang.Class({
 | 
			
		||||
    _getStatus: function() {
 | 
			
		||||
        let ap = this._device.active_access_point;
 | 
			
		||||
 | 
			
		||||
        if (ap)
 | 
			
		||||
        if (this._isHotSpotMaster())
 | 
			
		||||
            return _("Hotspot Active");
 | 
			
		||||
        else if (this._device.state >= NetworkManager.DeviceState.PREPARE &&
 | 
			
		||||
                 this._device.state < NetworkManager.DeviceState.ACTIVATED)
 | 
			
		||||
            return _("Connecting");
 | 
			
		||||
        else if (ap)
 | 
			
		||||
            return ssidToLabel(ap.get_ssid());
 | 
			
		||||
        else if (!this._client.wireless_hardware_enabled)
 | 
			
		||||
            return _("Hardware Disabled");
 | 
			
		||||
@@ -1131,20 +1271,52 @@ const NMDeviceWireless = new Lang.Class({
 | 
			
		||||
            return 'network-wireless-signal-none-symbolic';
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _canReachInternet: function() {
 | 
			
		||||
        if (this._client.primary_connection != this._device.active_connection)
 | 
			
		||||
            return true;
 | 
			
		||||
 | 
			
		||||
        return this._client.connectivity == NetworkManager.ConnectivityState.FULL;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _isHotSpotMaster: function() {
 | 
			
		||||
        if (!this._device.active_connection)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        let connection = this._settings.get_connection_by_path(this._device.active_connection.connection);
 | 
			
		||||
        if (!connection)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        let ip4config = connection.get_setting_ip4_config();
 | 
			
		||||
        if (!ip4config)
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        return ip4config.get_method() == NetworkManager.SETTING_IP4_CONFIG_METHOD_SHARED;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    getIndicatorIcon: function() {
 | 
			
		||||
        if (this._device.state >= NetworkManager.DeviceState.PREPARE &&
 | 
			
		||||
            this._device.state < NetworkManager.DeviceState.ACTIVATED)
 | 
			
		||||
        if (this._device.state < NetworkManager.DeviceState.PREPARE)
 | 
			
		||||
            return 'network-wireless-disconnected-symbolic';
 | 
			
		||||
        if (this._device.state < NetworkManager.DeviceState.ACTIVATED)
 | 
			
		||||
            return 'network-wireless-acquiring-symbolic';
 | 
			
		||||
 | 
			
		||||
        if (this._isHotSpotMaster())
 | 
			
		||||
            return 'network-wireless-hotspot-symbolic';
 | 
			
		||||
 | 
			
		||||
        let ap = this._device.active_access_point;
 | 
			
		||||
        if (!ap) {
 | 
			
		||||
            if (this._device.mode != NM80211Mode.ADHOC)
 | 
			
		||||
                log('An active wireless connection, in infrastructure mode, involves no access point?');
 | 
			
		||||
 | 
			
		||||
            return 'network-wireless-connected-symbolic';
 | 
			
		||||
            if (this._canReachInternet())
 | 
			
		||||
                return 'network-wireless-connected-symbolic';
 | 
			
		||||
            else
 | 
			
		||||
                return 'network-wireless-no-route-symbolic';
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return 'network-wireless-signal-' + signalToIcon(ap.strength) + '-symbolic';
 | 
			
		||||
        if (this._canReachInternet())
 | 
			
		||||
            return 'network-wireless-signal-' + signalToIcon(ap.strength) + '-symbolic';
 | 
			
		||||
        else
 | 
			
		||||
            return 'network-wireless-no-route-symbolic';
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(NMDeviceWireless.prototype);
 | 
			
		||||
@@ -1160,6 +1332,22 @@ const NMVPNConnectionItem = new Lang.Class({
 | 
			
		||||
        return this._activeConnection.vpn_state != NetworkManager.VPNConnectionState.DISCONNECTED;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _buildUI: function() {
 | 
			
		||||
        this.labelItem = new PopupMenu.PopupMenuItem('');
 | 
			
		||||
        this.labelItem.connect('activate', Lang.bind(this, this._toggle));
 | 
			
		||||
 | 
			
		||||
        this.radioItem = new PopupMenu.PopupSwitchMenuItem(this._connection.get_id(), false);
 | 
			
		||||
        this.radioItem.connect('toggled', Lang.bind(this, this._toggle));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let isActive = this.isActive();
 | 
			
		||||
        this.labelItem.label.text = isActive ? _("Turn Off") : this._section.getConnectLabel();
 | 
			
		||||
        this.radioItem.setToggleState(isActive);
 | 
			
		||||
        this.radioItem.setStatus(this._getStatus());
 | 
			
		||||
        this.emit('icon-changed');
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getStatus: function() {
 | 
			
		||||
        if (this._activeConnection == null)
 | 
			
		||||
            return null;
 | 
			
		||||
@@ -1229,19 +1417,53 @@ const NMVPNSection = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _init: function(client) {
 | 
			
		||||
        this.parent(client);
 | 
			
		||||
 | 
			
		||||
        this._vpnSettings = new PopupMenu.PopupMenuItem('');
 | 
			
		||||
        this.item.menu.addMenuItem(this._vpnSettings);
 | 
			
		||||
        this._vpnSettings.connect('activate', Lang.bind(this, this._onSettingsActivate));
 | 
			
		||||
 | 
			
		||||
        this._sync();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let nItems = this._connectionItems.size();
 | 
			
		||||
        let nItems = this._connectionItems.size;
 | 
			
		||||
        this.item.actor.visible = (nItems > 0);
 | 
			
		||||
 | 
			
		||||
        if (nItems > 1)
 | 
			
		||||
            this._vpnSettings.label.text = _("Network Settings");
 | 
			
		||||
        else
 | 
			
		||||
            this._vpnSettings.label.text = _("VPN Settings");
 | 
			
		||||
 | 
			
		||||
        this.parent();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _onSettingsActivate: function() {
 | 
			
		||||
        let nItems = this._connectionItems.size;
 | 
			
		||||
        if (nItems > 1) {
 | 
			
		||||
            let appSys = Shell.AppSystem.get_default();
 | 
			
		||||
            let app = appSys.lookup_app('gnome-network-panel.desktop');
 | 
			
		||||
            app.launch(0, -1);
 | 
			
		||||
        } else {
 | 
			
		||||
            let connection = this._connections[0];
 | 
			
		||||
            Util.spawnApp(['gnome-control-center', 'network', 'show-device',
 | 
			
		||||
                           connection.get_path()]);
 | 
			
		||||
        }
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getDescription: function() {
 | 
			
		||||
        return _("VPN");
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getStatus: function() {
 | 
			
		||||
        let values = this._connectionItems.values();
 | 
			
		||||
        for (let item of values) {
 | 
			
		||||
            if (item.isActive())
 | 
			
		||||
                return item.getName();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        return _("Off");
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _getMenuIcon: function() {
 | 
			
		||||
        return this.getIndicatorIcon() || 'network-vpn-symbolic';
 | 
			
		||||
    },
 | 
			
		||||
@@ -1255,9 +1477,10 @@ const NMVPNSection = new Lang.Class({
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    setActiveConnections: function(vpnConnections) {
 | 
			
		||||
        this._connectionItems.values().forEach(function(item) {
 | 
			
		||||
        let connections = this._connectionItems.values();
 | 
			
		||||
        for (let item of connections) {
 | 
			
		||||
            item.setActiveConnection(null);
 | 
			
		||||
        });
 | 
			
		||||
        }
 | 
			
		||||
        vpnConnections.forEach(Lang.bind(this, function(a) {
 | 
			
		||||
            let item = this._connectionItems.get(a._connection.get_uuid());
 | 
			
		||||
            item.setActiveConnection(a);
 | 
			
		||||
@@ -1270,8 +1493,7 @@ const NMVPNSection = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    getIndicatorIcon: function() {
 | 
			
		||||
        let items = this._connectionItems.values();
 | 
			
		||||
        for (let i = 0; i < items.length; i++) {
 | 
			
		||||
            let item = items[i];
 | 
			
		||||
        for (let item of items) {
 | 
			
		||||
            let icon = item.getIndicatorIcon();
 | 
			
		||||
            if (icon)
 | 
			
		||||
                return icon;
 | 
			
		||||
@@ -1293,6 +1515,7 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        // Device types
 | 
			
		||||
        this._dtypes = { };
 | 
			
		||||
        this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired;
 | 
			
		||||
        this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless;
 | 
			
		||||
        this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem;
 | 
			
		||||
        this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth;
 | 
			
		||||
@@ -1300,6 +1523,7 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        // Connection types
 | 
			
		||||
        this._ctypes = { };
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_WIRED_SETTING_NAME] = NMConnectionCategory.WIRED;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN;
 | 
			
		||||
        this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN;
 | 
			
		||||
@@ -1322,6 +1546,15 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
        this._tryLateInit();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _createDeviceCategory: function() {
 | 
			
		||||
        let category = {
 | 
			
		||||
            section: new PopupMenu.PopupMenuSection(),
 | 
			
		||||
            devices: [ ],
 | 
			
		||||
        };
 | 
			
		||||
        this.menu.addMenuItem(category.section);
 | 
			
		||||
        return category;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _tryLateInit: function() {
 | 
			
		||||
        if (!this._client || !this._settings)
 | 
			
		||||
            return;
 | 
			
		||||
@@ -1337,17 +1570,9 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
        this._nmDevices = [];
 | 
			
		||||
        this._devices = { };
 | 
			
		||||
 | 
			
		||||
        this._devices.wireless = {
 | 
			
		||||
            section: new PopupMenu.PopupMenuSection(),
 | 
			
		||||
            devices: [ ],
 | 
			
		||||
        };
 | 
			
		||||
        this.menu.addMenuItem(this._devices.wireless.section);
 | 
			
		||||
 | 
			
		||||
        this._devices.wwan = {
 | 
			
		||||
            section: new PopupMenu.PopupMenuSection(),
 | 
			
		||||
            devices: [ ],
 | 
			
		||||
        };
 | 
			
		||||
        this.menu.addMenuItem(this._devices.wwan.section);
 | 
			
		||||
        this._devices.wired = this._createDeviceCategory();
 | 
			
		||||
        this._devices.wireless = this._createDeviceCategory();
 | 
			
		||||
        this._devices.wwan = this._createDeviceCategory();
 | 
			
		||||
 | 
			
		||||
        this._vpnSection = new NMVPNSection(this._client);
 | 
			
		||||
        this._vpnSection.connect('activation-failed', Lang.bind(this, this._onActivationFailed));
 | 
			
		||||
@@ -1591,7 +1816,7 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
    _connectionRemoved: function(connection) {
 | 
			
		||||
        let pos = this._connections.indexOf(connection);
 | 
			
		||||
        if (pos != -1)
 | 
			
		||||
            this._connections.splice(connection, 1);
 | 
			
		||||
            this._connections.splice(pos, 1);
 | 
			
		||||
 | 
			
		||||
        let section = connection._section;
 | 
			
		||||
 | 
			
		||||
@@ -1617,7 +1842,6 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
        let connectionSettings = connection.get_setting_by_name(NetworkManager.SETTING_CONNECTION_SETTING_NAME);
 | 
			
		||||
        connection._type = connectionSettings.type;
 | 
			
		||||
        connection._section = this._ctypes[connection._type] || NMConnectionCategory.INVALID;
 | 
			
		||||
        connection._timestamp = connectionSettings.timestamp;
 | 
			
		||||
 | 
			
		||||
        let section = connection._section;
 | 
			
		||||
 | 
			
		||||
@@ -1642,8 +1866,7 @@ const NMApplet = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
    _updateIcon: function() {
 | 
			
		||||
        if (!this._client.networking_enabled || !this._mainConnection) {
 | 
			
		||||
            this._primaryIndicator.icon_name = 'network-offline-symbolic';
 | 
			
		||||
            this._primaryIndicator.visible = true;
 | 
			
		||||
            this._primaryIndicator.visible = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            let dev = this._mainConnection._primaryDevice;
 | 
			
		||||
            this._primaryIndicator.visible = (dev != null);
 | 
			
		||||
 
 | 
			
		||||
@@ -8,20 +8,22 @@ const Main = imports.ui.main;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Power';
 | 
			
		||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Power';
 | 
			
		||||
const BUS_NAME = 'org.freedesktop.UPower';
 | 
			
		||||
const OBJECT_PATH = '/org/freedesktop/UPower/devices/DisplayDevice';
 | 
			
		||||
 | 
			
		||||
const PowerManagerInterface = <interface name="org.gnome.SettingsDaemon.Power">
 | 
			
		||||
<method name="GetDevices">
 | 
			
		||||
    <arg type="a(susdut)" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<method name="GetPrimaryDevice">
 | 
			
		||||
    <arg type="(susdut)" direction="out" />
 | 
			
		||||
</method>
 | 
			
		||||
<property name="Icon" type="s" access="read" />
 | 
			
		||||
</interface>;
 | 
			
		||||
const DisplayDeviceInterface = '<node> \
 | 
			
		||||
<interface name="org.freedesktop.UPower.Device"> \
 | 
			
		||||
  <property name="Type" type="u" access="read"/> \
 | 
			
		||||
  <property name="State" type="u" access="read"/> \
 | 
			
		||||
  <property name="Percentage" type="d" access="read"/> \
 | 
			
		||||
  <property name="TimeToEmpty" type="x" access="read"/> \
 | 
			
		||||
  <property name="TimeToFull" type="x" access="read"/> \
 | 
			
		||||
  <property name="IsPresent" type="b" access="read"/> \
 | 
			
		||||
  <property name="IconName" type="s" access="read"/> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(PowerManagerInterface);
 | 
			
		||||
const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(DisplayDeviceInterface);
 | 
			
		||||
 | 
			
		||||
const Indicator = new Lang.Class({
 | 
			
		||||
    Name: 'PowerIndicator',
 | 
			
		||||
@@ -32,7 +34,7 @@ const Indicator = new Lang.Class({
 | 
			
		||||
 | 
			
		||||
        this._indicator = this._addIndicator();
 | 
			
		||||
 | 
			
		||||
        this._proxy = new PowerManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
 | 
			
		||||
        this._proxy = new PowerManagerProxy(Gio.DBus.system, BUS_NAME, OBJECT_PATH,
 | 
			
		||||
                                            Lang.bind(this, function(proxy, error) {
 | 
			
		||||
                                                if (error) {
 | 
			
		||||
                                                    log(error.message);
 | 
			
		||||
@@ -43,7 +45,7 @@ const Indicator = new Lang.Class({
 | 
			
		||||
                                                this._sync();
 | 
			
		||||
                                            }));
 | 
			
		||||
 | 
			
		||||
        this._item = new PopupMenu.PopupSubMenuMenuItem(_("Battery"), true);
 | 
			
		||||
        this._item = new PopupMenu.PopupSubMenuMenuItem("", true);
 | 
			
		||||
        this._item.menu.addSettingsAction(_("Power Settings"), 'gnome-power-panel.desktop');
 | 
			
		||||
        this.menu.addMenuItem(this._item);
 | 
			
		||||
 | 
			
		||||
@@ -56,11 +58,18 @@ const Indicator = new Lang.Class({
 | 
			
		||||
        this.menu.setSensitive(sensitive);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _statusForDevice: function(device) {
 | 
			
		||||
        let [device_id, device_type, icon, percentage, state, seconds] = device;
 | 
			
		||||
    _getStatus: function() {
 | 
			
		||||
        let seconds = 0;
 | 
			
		||||
 | 
			
		||||
        if (state == UPower.DeviceState.FULLY_CHARGED)
 | 
			
		||||
        if (this._proxy.State == UPower.DeviceState.FULLY_CHARGED)
 | 
			
		||||
            return _("Fully Charged");
 | 
			
		||||
        else if (this._proxy.State == UPower.DeviceState.CHARGING)
 | 
			
		||||
            seconds = this._proxy.TimeToFull;
 | 
			
		||||
        else if (this._proxy.State == UPower.DeviceState.DISCHARGING)
 | 
			
		||||
            seconds = this._proxy.TimeToEmpty;
 | 
			
		||||
        // state is one of PENDING_CHARGING, PENDING_DISCHARGING
 | 
			
		||||
        else
 | 
			
		||||
            return _("Estimating…");
 | 
			
		||||
 | 
			
		||||
        let time = Math.round(seconds / 60);
 | 
			
		||||
        if (time == 0) {
 | 
			
		||||
@@ -72,44 +81,43 @@ const Indicator = new Lang.Class({
 | 
			
		||||
        let minutes = time % 60;
 | 
			
		||||
        let hours = Math.floor(time / 60);
 | 
			
		||||
 | 
			
		||||
        if (state == UPower.DeviceState.DISCHARGING) {
 | 
			
		||||
        if (this._proxy.State == UPower.DeviceState.DISCHARGING) {
 | 
			
		||||
            // Translators: this is <hours>:<minutes> Remaining (<percentage>)
 | 
			
		||||
            return _("%d\u2236%02d Remaining (%d%%)").format(hours, minutes, percentage);
 | 
			
		||||
            return _("%d\u2236%02d Remaining (%d%%)").format(hours, minutes, this._proxy.Percentage);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (state == UPower.DeviceState.CHARGING) {
 | 
			
		||||
        if (this._proxy.State == UPower.DeviceState.CHARGING) {
 | 
			
		||||
            // Translators: this is <hours>:<minutes> Until Full (<percentage>)
 | 
			
		||||
            return _("%d\u2236%02d Until Full (%d%%)").format(hours, minutes, percentage);
 | 
			
		||||
            return _("%d\u2236%02d Until Full (%d%%)").format(hours, minutes, this._proxy.Percentage);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // state is one of PENDING_CHARGING, PENDING_DISCHARGING
 | 
			
		||||
        return _("Estimating…");
 | 
			
		||||
        return null;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        function isBattery(result) {
 | 
			
		||||
            if (!result)
 | 
			
		||||
                return false;
 | 
			
		||||
 | 
			
		||||
            let [device] = result;
 | 
			
		||||
            let [, deviceType] = device;
 | 
			
		||||
            return (deviceType == UPower.DeviceKind.BATTERY);
 | 
			
		||||
        // Do we have batteries or a UPS?
 | 
			
		||||
        let visible = this._proxy.IsPresent;
 | 
			
		||||
        if (visible) {
 | 
			
		||||
            this._item.actor.show();
 | 
			
		||||
        } else {
 | 
			
		||||
            // If there's no battery, then we use the power icon.
 | 
			
		||||
            this._item.actor.hide();
 | 
			
		||||
            this._indicator.icon_name = 'system-shutdown-symbolic';
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        this._proxy.GetPrimaryDeviceRemote(Lang.bind(this, function(result, error) {
 | 
			
		||||
            if (isBattery(result)) {
 | 
			
		||||
                let [device] = result;
 | 
			
		||||
                let [,, icon] = device;
 | 
			
		||||
                let gicon = Gio.icon_new_for_string(icon);
 | 
			
		||||
                this._indicator.gicon = gicon;
 | 
			
		||||
                this._item.icon.gicon = gicon;
 | 
			
		||||
                this._item.status.text = this._statusForDevice(device);
 | 
			
		||||
                this._item.actor.show();
 | 
			
		||||
            } else {
 | 
			
		||||
                // If there's no battery, then we use the power icon.
 | 
			
		||||
                this._indicator.icon_name = 'system-shutdown-symbolic';
 | 
			
		||||
                this._item.actor.hide();
 | 
			
		||||
            }
 | 
			
		||||
        }));
 | 
			
		||||
        // The icons
 | 
			
		||||
        let icon = this._proxy.IconName;
 | 
			
		||||
        this._indicator.icon_name = icon;
 | 
			
		||||
        this._item.icon.icon_name = icon;
 | 
			
		||||
 | 
			
		||||
        // The status label
 | 
			
		||||
        this._item.status.text = this._getStatus();
 | 
			
		||||
 | 
			
		||||
        // The sub-menu heading
 | 
			
		||||
        if (this._proxy.Type == UPower.DeviceKind.UPS)
 | 
			
		||||
            this._item.label.text = _("UPS");
 | 
			
		||||
        else
 | 
			
		||||
            this._item.label.text = _("Battery");
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
@@ -2,19 +2,67 @@
 | 
			
		||||
 | 
			
		||||
const Gio = imports.gi.Gio;
 | 
			
		||||
const Lang = imports.lang;
 | 
			
		||||
const Signals = imports.signals;
 | 
			
		||||
 | 
			
		||||
const Main = imports.ui.main;
 | 
			
		||||
const PanelMenu = imports.ui.panelMenu;
 | 
			
		||||
const PopupMenu = imports.ui.popupMenu;
 | 
			
		||||
 | 
			
		||||
const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill';
 | 
			
		||||
const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill';
 | 
			
		||||
 | 
			
		||||
const RfkillManagerInterface = <interface name="org.gnome.SettingsDaemon.Rfkill">
 | 
			
		||||
<property name="AirplaneMode" type="b" access="readwrite" />
 | 
			
		||||
</interface>;
 | 
			
		||||
const RfkillManagerInterface = '<node> \
 | 
			
		||||
<interface name="org.gnome.SettingsDaemon.Rfkill"> \
 | 
			
		||||
<property name="AirplaneMode" type="b" access="readwrite" /> \
 | 
			
		||||
<property name="HardwareAirplaneMode" type="b" access="read" /> \
 | 
			
		||||
</interface> \
 | 
			
		||||
</node>';
 | 
			
		||||
 | 
			
		||||
const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface);
 | 
			
		||||
 | 
			
		||||
const RfkillManager = new Lang.Class({
 | 
			
		||||
    Name: 'RfkillManager',
 | 
			
		||||
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
 | 
			
		||||
                                             Lang.bind(this, function(proxy, error) {
 | 
			
		||||
                                                 if (error) {
 | 
			
		||||
                                                     log(error.message);
 | 
			
		||||
                                                     return;
 | 
			
		||||
                                                 }
 | 
			
		||||
                                                 this._proxy.connect('g-properties-changed',
 | 
			
		||||
                                                                     Lang.bind(this, this._changed));
 | 
			
		||||
                                                 this._changed();
 | 
			
		||||
                                             }));
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get airplaneMode() {
 | 
			
		||||
        return this._proxy.AirplaneMode;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    set airplaneMode(v) {
 | 
			
		||||
        this._proxy.AirplaneMode = v;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    get hwAirplaneMode() {
 | 
			
		||||
        return this._proxy.HardwareAirplaneMode;
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _changed: function() {
 | 
			
		||||
        this.emit('airplane-mode-changed');
 | 
			
		||||
    }
 | 
			
		||||
});
 | 
			
		||||
Signals.addSignalMethods(RfkillManager.prototype);
 | 
			
		||||
 | 
			
		||||
var _manager;
 | 
			
		||||
function getRfkillManager() {
 | 
			
		||||
    if (_manager != null)
 | 
			
		||||
        return _manager;
 | 
			
		||||
 | 
			
		||||
    _manager = new RfkillManager();
 | 
			
		||||
    return _manager;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const Indicator = new Lang.Class({
 | 
			
		||||
    Name: 'RfkillIndicator',
 | 
			
		||||
    Extends: PanelMenu.SystemIndicator,
 | 
			
		||||
@@ -22,16 +70,8 @@ const Indicator = new Lang.Class({
 | 
			
		||||
    _init: function() {
 | 
			
		||||
        this.parent();
 | 
			
		||||
 | 
			
		||||
        this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH,
 | 
			
		||||
                                             Lang.bind(this, function(proxy, error) {
 | 
			
		||||
                                                 if (error) {
 | 
			
		||||
                                                     log(error.message);
 | 
			
		||||
                                                     return;
 | 
			
		||||
                                                 }
 | 
			
		||||
                                                 this._proxy.connect('g-properties-changed',
 | 
			
		||||
                                                                     Lang.bind(this, this._sync));
 | 
			
		||||
                                                 this._sync();
 | 
			
		||||
                                             }));
 | 
			
		||||
        this._manager = getRfkillManager();
 | 
			
		||||
        this._manager.connect('airplane-mode-changed', Lang.bind(this, this._sync));
 | 
			
		||||
 | 
			
		||||
        this._indicator = this._addIndicator();
 | 
			
		||||
        this._indicator.icon_name = 'airplane-mode-symbolic';
 | 
			
		||||
@@ -43,16 +83,34 @@ const Indicator = new Lang.Class({
 | 
			
		||||
        this._item = new PopupMenu.PopupSubMenuMenuItem(_("Airplane Mode"), true);
 | 
			
		||||
        this._item.icon.icon_name = 'airplane-mode-symbolic';
 | 
			
		||||
        this._item.status.text = _("On");
 | 
			
		||||
        this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
 | 
			
		||||
            this._proxy.AirplaneMode = false;
 | 
			
		||||
        this._offItem = this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() {
 | 
			
		||||
            this._manager.airplaneMode = false;
 | 
			
		||||
        }));
 | 
			
		||||
        this._item.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop');
 | 
			
		||||
        this.menu.addMenuItem(this._item);
 | 
			
		||||
 | 
			
		||||
        Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated));
 | 
			
		||||
        this._sessionUpdated();
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sessionUpdated: function() {
 | 
			
		||||
        let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter;
 | 
			
		||||
        this.menu.setSensitive(sensitive);
 | 
			
		||||
    },
 | 
			
		||||
 | 
			
		||||
    _sync: function() {
 | 
			
		||||
        let airplaneMode = this._proxy.AirplaneMode;
 | 
			
		||||
        let airplaneMode = this._manager.airplaneMode;
 | 
			
		||||
        let hwAirplaneMode = this._manager.hwAirplaneMode;
 | 
			
		||||
        let changed = (airplaneMode != this._indicator.visible) ||
 | 
			
		||||
            (hwAirplaneMode != this._offItem.actor.visible);
 | 
			
		||||
 | 
			
		||||
        this._indicator.visible = airplaneMode;
 | 
			
		||||
        this._item.actor.visible = airplaneMode;
 | 
			
		||||
        this._offItem.setSensitive(!hwAirplaneMode);
 | 
			
		||||
 | 
			
		||||
        if (hwAirplaneMode)
 | 
			
		||||
            this._offItem.label.text = _("Use hardware switch to turn off");
 | 
			
		||||
        else
 | 
			
		||||
            this._offItem.label.text = _("Turn Off");
 | 
			
		||||
    },
 | 
			
		||||
});
 | 
			
		||||
 
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user