Compare commits
	
		
			557 Commits
		
	
	
		
			3.15.91
			...
			wip/loc-ap
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 11db28b216 | ||
|   | 614f37b116 | ||
|   | 779b1ae8e5 | ||
|   | 346ffd14d7 | ||
|   | 558b51f555 | ||
|   | ef18b7ea25 | ||
|   | 0044e225aa | ||
|   | c25c143b24 | ||
|   | b87da87252 | ||
|   | f9258bb5e3 | ||
|   | 3d747b00e6 | ||
|   | 450345b4d0 | ||
|   | 7563e1ebcd | ||
|   | f96cc4dd69 | ||
|   | c65a9c4d2e | ||
|   | c01bd37edc | ||
|   | 021cecbce2 | ||
|   | 2a0cb7ff05 | ||
|   | 0085a94706 | ||
|   | 5db38194dc | ||
|   | 2a26143149 | ||
|   | 62f22557aa | ||
|   | 516ea5a02e | ||
|   | 9154471aba | ||
|   | fbf6746acf | ||
|   | a36686a6aa | ||
|   | fd837d74d1 | ||
|   | fd57334395 | ||
|   | 85100cb65f | ||
|   | c70f6278d6 | ||
|   | e573441bac | ||
|   | 5ed9571b37 | ||
|   | 9d203ddc0f | ||
|   | 508a13ae72 | ||
|   | c0b50cbdf2 | ||
|   | 06f78549bd | ||
|   | 67afd7a6d8 | ||
|   | ffa8c2f2b1 | ||
|   | 3803a880e8 | ||
|   | 965aedb0bb | ||
|   | 80911535a7 | ||
|   | ca401d5036 | ||
|   | a1c091d98d | ||
|   | e1e08f0a68 | ||
|   | 1a4f629554 | ||
|   | 4113be770b | ||
|   | cf3f4850b8 | ||
|   | 14f374096a | ||
|   | 6fef5c37f7 | ||
|   | 6e7455aa1e | ||
|   | 03bf6fa399 | ||
|   | f777e761c0 | ||
|   | 31201d9618 | ||
|   | d95d78ac15 | ||
|   | bef4f17c49 | ||
|   | f4b7ab0cb6 | ||
|   | a180dde01c | ||
|   | 385c918e2e | ||
|   | d7401c8646 | ||
|   | a456d5eb19 | ||
|   | 6c08799c7b | ||
|   | 8d7bb6496c | ||
|   | f0496a2d3c | ||
|   | 6664553b7e | ||
|   | 2a950ca3b3 | ||
|   | 463cd6382c | ||
|   | 78db025b10 | ||
|   | f3265c28a9 | ||
|   | a6e5e459d3 | ||
|   | 9ba399bf18 | ||
|   | a52c91e9e5 | ||
|   | 9a7b47c23f | ||
|   | 18f7d20006 | ||
|   | 3f0ee88657 | ||
|   | c634718dfa | ||
|   | df6b31de05 | ||
|   | b8e29ae8c7 | ||
|   | cdba8e5cea | ||
|   | 4fccdaafb7 | ||
|   | df0b465e76 | ||
|   | aacdd4fd5e | ||
|   | 5f68c3a324 | ||
|   | 8ceae3b054 | ||
|   | 3fc5afaff1 | ||
|   | 8b7464c648 | ||
|   | 3e602b1765 | ||
|   | 5858028411 | ||
|   | 2c682ace81 | ||
|   | 45f3106814 | ||
|   | aa947f9948 | ||
|   | 0c72d1fcbd | ||
|   | 6d22670307 | ||
|   | 0b9e68e305 | ||
|   | da8155cbe5 | ||
|   | 5b7a052e18 | ||
|   | 6f26e39082 | ||
|   | 489b96a310 | ||
|   | e65d90d624 | ||
|   | 9c74e22313 | ||
|   | 48a1fce151 | ||
|   | 83e7f6f496 | ||
|   | 3c5c3a6597 | ||
|   | 1c3ea1649f | ||
|   | 48a54e8ac4 | ||
|   | 113a854048 | ||
|   | 882f5fa79e | ||
|   | 9acdb8012c | ||
|   | 731d64e0e4 | ||
|   | ccf1bd9f27 | ||
|   | c164a8fe03 | ||
|   | 1a39666f7c | ||
|   | f2731d4d6a | ||
|   | 3e63fb7abe | ||
|   | 8b4249ef26 | ||
|   | bf0be6ef12 | ||
|   | 294702d3f1 | ||
|   | 58f3b7c748 | ||
|   | e25502aeb2 | ||
|   | ffe4eaf00d | ||
|   | 2f88a7a1e1 | ||
|   | cd7d564125 | ||
|   | ede81017ec | ||
|   | a539e6236a | ||
|   | 90b7710834 | ||
|   | f8cc8f1dc1 | ||
|   | f8e5e3e435 | ||
|   | 508e751ffd | ||
|   | 207c847762 | ||
|   | 3c980566d3 | ||
|   | 50b59e0ca6 | ||
|   | 14c52bb00a | ||
|   | 9720b32987 | ||
|   | 674ae262c8 | ||
|   | db297e7fdb | ||
|   | d57c146514 | ||
|   | ec5a4328e3 | ||
|   | e4ee72c481 | ||
|   | 31f1e9ff0a | ||
|   | d6c049a8c9 | ||
|   | 44047ac881 | ||
|   | 464d5d8a13 | ||
|   | 29811a85dc | ||
|   | 36ee4e6c3b | ||
|   | 2036e4c85c | ||
|   | f24034de84 | ||
|   | 522ff86081 | ||
|   | 669e3c8ed9 | ||
|   | ce850f464c | ||
|   | 03eaa61cef | ||
|   | e10e953d24 | ||
|   | 2b47bb3d82 | ||
|   | 73d819116c | ||
|   | c8dd984663 | ||
|   | 6087eb6d0e | ||
|   | 97b43d1d36 | ||
|   | 57ebadbaf8 | ||
|   | cad7bb1151 | ||
|   | 9a376d47c5 | ||
|   | b79adc05f4 | ||
|   | f765c5e319 | ||
|   | edc445c0c9 | ||
|   | ab6b0f3f7f | ||
|   | 5e7902e733 | ||
|   | f9f821aa55 | ||
|   | 9ed4b2a5ae | ||
|   | 0130ced790 | ||
|   | 8dab07af82 | ||
|   | f6cd3fa5ed | ||
|   | faae1a028e | ||
|   | f5e32184fe | ||
|   | 18c7138237 | ||
|   | 3f0fbae7e2 | ||
|   | 7f1a258ff9 | ||
|   | 35b38d5cb2 | ||
|   | 09e8a437d4 | ||
|   | d2bedcc182 | ||
|   | 84eda6e459 | ||
|   | f5e7530fc7 | ||
|   | f983b34784 | ||
|   | 36bbe64898 | ||
|   | 627a393ed6 | ||
|   | a025b151ef | ||
|   | 18b6f13395 | ||
|   | 051413550f | ||
|   | 3e10574736 | ||
|   | 9a3041004b | ||
|   | 87f71b8ce1 | ||
|   | 825146f1e3 | ||
|   | 52995416fd | ||
|   | 6c43d0247a | ||
|   | 9aa98d9f0c | ||
|   | c3a29d6df1 | ||
|   | 82f84416a9 | ||
|   | 9dd3162dbe | ||
|   | 7ef519756a | ||
|   | 3bbe74d1c1 | ||
|   | 409f6718b8 | ||
|   | 9c0e179080 | ||
|   | b3b278d41f | ||
|   | 0f466dbafb | ||
|   | fbb4a9a3a6 | ||
|   | 8ddae5cd71 | ||
|   | b0915c7b60 | ||
|   | 831bb4e334 | ||
|   | 4e025506fa | ||
|   | abccf451bf | ||
|   | 14954117c0 | ||
|   | 629f408fe5 | ||
|   | 86c6ab3c01 | ||
|   | 4a6ff94701 | ||
|   | e480b08d58 | ||
|   | caf53861d1 | ||
|   | d0480648ba | ||
|   | eb8cfe799f | ||
|   | b9f2541880 | ||
|   | bde9b08bfe | ||
|   | 8a4c862633 | ||
|   | 785c90f4b8 | ||
|   | dd6a11e4c7 | ||
|   | 64e9503adb | ||
|   | 36c885bf34 | ||
|   | ad7cde805d | ||
|   | 2c2c67f4dc | ||
|   | cc4f8dfab0 | ||
|   | 0fb13608c5 | ||
|   | 09dbe17da0 | ||
|   | fdd347c9aa | ||
|   | 572095515b | ||
|   | f2d4aa0822 | ||
|   | 030a22d795 | ||
|   | c70afcdb44 | ||
|   | 526d6c03b8 | ||
|   | 261b55300d | ||
|   | e13bfd9a17 | ||
|   | e096d18bac | ||
|   | 9460f0e4f3 | ||
|   | e6591f52ac | ||
|   | 07e3d1fd5c | ||
|   | 1fbc6b24c8 | ||
|   | 982777be94 | ||
|   | 6610a34ad0 | ||
|   | bfa8a0441a | ||
|   | 7723622ec7 | ||
|   | 6bcc8c70ef | ||
|   | d114d5f95a | ||
|   | b5c734da42 | ||
|   | 2077bb94c1 | ||
|   | 65a4ee7fb4 | ||
|   | debf293298 | ||
|   | a0df3e7d1e | ||
|   | 0d67c2d164 | ||
|   | 682bd7b622 | ||
|   | fa0e54edbb | ||
|   | 5a0b209663 | ||
|   | 7e8859fd0e | ||
|   | 444fa2e0ab | ||
|   | a31455b921 | ||
|   | ac0213a516 | ||
|   | 45a6e2c305 | ||
|   | 11cbd396c0 | ||
|   | a343445cd2 | ||
|   | be3d62487c | ||
|   | 58905bd01a | ||
|   | 08506eac2d | ||
|   | 02c6b0374d | ||
|   | 0722c06275 | ||
|   | 17a4044d97 | ||
|   | 27a7194634 | ||
|   | d73f560bcc | ||
|   | e92f43b83e | ||
|   | fed79ce4e6 | ||
|   | fc45cf03bf | ||
|   | efde11a0f3 | ||
|   | fb951ff9b5 | ||
|   | f5865e895e | ||
|   | b8c2d4c6c7 | ||
|   | 778ad49ab4 | ||
|   | fe7dd1305f | ||
|   | 378a3df5ea | ||
|   | e63b81d69c | ||
|   | c2fa2cdd8a | ||
|   | 6f215427f8 | ||
|   | 67ed4e0570 | ||
|   | 8a15178557 | ||
|   | f3ecfab378 | ||
|   | 804563d5b2 | ||
|   | c3e5d983b9 | ||
|   | d21edcfed5 | ||
|   | 9b69a45eee | ||
|   | 7424ee755a | ||
|   | ff664fd1d8 | ||
|   | fd3f03580d | ||
|   | a09150846a | ||
|   | f2a9c55637 | ||
|   | eaa3f83e46 | ||
|   | e786cc1454 | ||
|   | cd0c632fcb | ||
|   | d5f248cb82 | ||
|   | 6a800abe06 | ||
|   | fe265554a7 | ||
|   | 7305466765 | ||
|   | 9ac55a98f1 | ||
|   | a1149fb6ad | ||
|   | dfc4cc4aaf | ||
|   | ef7541291b | ||
|   | 248a3e6b7e | ||
|   | 6b1e381750 | ||
|   | feaf6108f9 | ||
|   | 9ad104585d | ||
|   | 7c44af3616 | ||
|   | 0599bf41b0 | ||
|   | eac303f84c | ||
|   | 7bdd1c625c | ||
|   | 0003760fd9 | ||
|   | eafb8c8e38 | ||
|   | 60c8105559 | ||
|   | 54626c6f7e | ||
|   | cca528a630 | ||
|   | 530193a3a2 | ||
|   | 52e3149040 | ||
|   | ad297ea9dc | ||
|   | 2015fc97dc | ||
|   | 35889a0f7d | ||
|   | dcd84a4b53 | ||
|   | 01374989b1 | ||
|   | f300462003 | ||
|   | 1e4da1b99c | ||
|   | e1de6cb98d | ||
|   | 59a18c4ead | ||
|   | b881e4b62a | ||
|   | 7060ae077b | ||
|   | a7b0910566 | ||
|   | 60706f72d4 | ||
|   | 2e77f6b34b | ||
|   | 50d5030949 | ||
|   | 03dbb0f931 | ||
|   | 249619fabd | ||
|   | 60d1f7797c | ||
|   | dc4b8c876e | ||
|   | 1724723e63 | ||
|   | 02455b1e28 | ||
|   | 47a9b97f8b | ||
|   | 0aa29daa72 | ||
|   | 182b1c1941 | ||
|   | bbc8010de3 | ||
|   | 15baa56584 | ||
|   | a72683707f | ||
|   | f4baa4ddf8 | ||
|   | f9eb36434f | ||
|   | bc5e16bcea | ||
|   | 86e04048ff | ||
|   | d7c0ff5e89 | ||
|   | 51e1efa277 | ||
|   | 67f636cc68 | ||
|   | fcdfebd0e7 | ||
|   | 069ec3b910 | ||
|   | 1092f55b54 | ||
|   | 8a8abf12f9 | ||
|   | f044e29526 | ||
|   | 4f703019ca | ||
|   | 0e6baec350 | ||
|   | 08690d658f | ||
|   | 674325e96c | ||
|   | f8aa486ad1 | ||
|   | be78f0f36a | ||
|   | 9917f05be8 | ||
|   | d23228522c | ||
|   | 8c9896561e | ||
|   | 23cdb2125e | ||
|   | 43fc598bd5 | ||
|   | 2105d2f952 | ||
|   | d18ec919dd | ||
|   | da3e5f9746 | ||
|   | 594a227bc1 | ||
|   | 7277744dc0 | ||
|   | e4718d3f7f | ||
|   | 5afd04781c | ||
|   | a0868bac6b | ||
|   | 265b1f0292 | ||
|   | 2c12f3a509 | ||
|   | 0141a2be9e | ||
|   | 6660342d2f | ||
|   | 92667e3b7f | ||
|   | a0632e3e02 | ||
|   | c6d2946ce6 | ||
|   | 86304418a9 | ||
|   | 2f228e21da | ||
|   | 8521556723 | ||
|   | 9f5ac0e6d5 | ||
|   | cc38dd1d49 | ||
|   | 66a5c0c094 | ||
|   | 8e802fd32f | ||
|   | e87656af16 | ||
|   | 8327dbd611 | ||
|   | e1b575dddc | ||
|   | 018025dada | ||
|   | 49fe0335ee | ||
|   | bbe417121c | ||
|   | 0068098996 | ||
|   | 36d9cc329d | ||
|   | 1f2e53dd15 | ||
|   | 048a14f1f3 | ||
|   | fd6ef482f0 | ||
|   | d8926b96e2 | ||
|   | 934ec3c3fe | ||
|   | 216e996f66 | ||
|   | a4b5583995 | ||
|   | b00a1d6b7f | ||
|   | d24abab4a8 | ||
|   | 23a9fb0314 | ||
|   | 5e26d0c90c | ||
|   | aeb971c33a | ||
|   | 8897385714 | ||
|   | e2a17fa8b4 | ||
|   | 14da6b68dc | ||
|   | 9a01a7ae07 | ||
|   | c3bf4a325d | ||
|   | 2d71456944 | ||
|   | 9934529a0b | ||
|   | ef6e8f5bb2 | ||
|   | 2ce7a3baa6 | ||
|   | c0d224e200 | ||
|   | 4b2e5a4375 | ||
|   | fbecbca9a0 | ||
|   | c312891774 | ||
|   | 486d4dfb68 | ||
|   | 169b00aa64 | ||
|   | f2c1a416bf | ||
|   | 28ef88911c | ||
|   | e2161f385d | ||
|   | f0c0687b43 | ||
|   | e76e874093 | ||
|   | d2a0cfb6d4 | ||
|   | e5270cb6ec | ||
|   | ee360d8749 | ||
|   | bb4e6fd2da | ||
|   | 651600ac19 | ||
|   | 67f4cf3cff | ||
|   | 9b76e9f90a | ||
|   | ba919b9c6a | ||
|   | 4da2862f8f | ||
|   | b1de1ada25 | ||
|   | 1002bbc212 | ||
|   | 15e42c4d5f | ||
|   | bb61dd4b44 | ||
|   | 2d4ba30ba2 | ||
|   | 54f46e8486 | ||
|   | 0ee762263a | ||
|   | 826682cc07 | ||
|   | 8b6e566728 | ||
|   | e4a20e51e2 | ||
|   | 173ff91640 | ||
|   | 438d0d4feb | ||
|   | 8f275a79a9 | ||
|   | a504dd866b | ||
|   | dfc51ef243 | ||
|   | a25d2dc256 | ||
|   | b2ae945cf2 | ||
|   | 681861c8c7 | ||
|   | 6d40cb98e7 | ||
|   | 81d7ab1e49 | ||
|   | 2212b4ea71 | ||
|   | 5650355da5 | ||
|   | 20a9206919 | ||
|   | 3533b2a8bc | ||
|   | b8d2238ff0 | ||
|   | 96c8870820 | ||
|   | 177e9316b0 | ||
|   | 27b7e6ef4b | ||
|   | 32d686d90f | ||
|   | f812e9be7d | ||
|   | c7185d597b | ||
|   | 6380526c12 | ||
|   | fa4f6c4561 | ||
|   | a5b7eaec1a | ||
|   | d165295c83 | ||
|   | 5f5874f8ca | ||
|   | ab5e950cd0 | ||
|   | 3e760f5ca6 | ||
|   | 9e8a24467e | ||
|   | d84d1f79a0 | ||
|   | 024ec36cc0 | ||
|   | 51b1258866 | ||
|   | ff1b76f4c7 | ||
|   | 0389ae5299 | ||
|   | 35ab60b712 | ||
|   | 0ea15f70c3 | ||
|   | f1c7a36dde | ||
|   | cc9093f118 | ||
|   | 775a2ea051 | ||
|   | cb3d5c2b51 | ||
|   | dc1c31d704 | ||
|   | 777616d8b0 | ||
|   | 8aeebbbf78 | ||
|   | dade67ba5a | ||
|   | 921a9071a1 | ||
|   | 90a08ba0b6 | ||
|   | 4e52ed9df7 | ||
|   | 01b51cd081 | ||
|   | d1efc274e5 | ||
|   | d48d787c1e | ||
|   | e4ad31a5dd | ||
|   | b61cb92053 | ||
|   | e72450f5d8 | ||
|   | d9211b8e20 | ||
|   | c16b83fc3e | ||
|   | 8b5a44e119 | ||
|   | b0be6b8678 | ||
|   | bb73547acf | ||
|   | d8fc58e174 | ||
|   | b9fdffbb62 | ||
|   | e48a83fcfc | ||
|   | 4c7eae7ef2 | ||
|   | 75745fc23f | ||
|   | 82479db084 | ||
|   | 9becb3985d | ||
|   | 4cb7df67b7 | ||
|   | e0a12f55b9 | ||
|   | b18240370d | ||
|   | 4253df6463 | ||
|   | d8b43865a2 | ||
|   | 3eb8b9ef68 | ||
|   | f96077bd0f | ||
|   | 741846d1ff | ||
|   | f36d7bd078 | ||
|   | bd51c92d65 | ||
|   | 2aed6ade79 | ||
|   | 83e5ea4827 | ||
|   | 5915348396 | ||
|   | 567dc70c9b | ||
|   | 8323891294 | ||
|   | 5bf8695295 | ||
|   | 7012437929 | ||
|   | 83ad98d640 | ||
|   | 1816f763d2 | ||
|   | 1a01f07b2c | ||
|   | 23d48fb334 | ||
|   | dada0420b1 | ||
|   | 17a336c795 | ||
|   | 8c347965d7 | ||
|   | e1816cd228 | ||
|   | 3614da6845 | ||
|   | 8aa1765f24 | ||
|   | 8eb0782f25 | ||
|   | 5f6aba7f4b | ||
|   | 986b14fb1b | ||
|   | 7c03b88b74 | ||
|   | 1f277ebcb9 | ||
|   | 8c54bc68e0 | ||
|   | 6e39be5b19 | ||
|   | cf71ea016f | ||
|   | 4affa5528a | ||
|   | 8536f9312c | ||
|   | a0e8456cb5 | ||
|   | 77074b5646 | ||
|   | cd68ed8297 | ||
|   | eaef067b3e | 
							
								
								
									
										6
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						| @@ -17,10 +17,8 @@ config.status | ||||
| config | ||||
| configure | ||||
| data/50-gnome-shell-*.xml | ||||
| data/gnome-shell.desktop | ||||
| data/gnome-shell.desktop.in | ||||
| data/gnome-shell-wayland.desktop | ||||
| data/gnome-shell-wayland.desktop.in | ||||
| data/org.gnome.Shell.desktop | ||||
| data/org.gnome.Shell.desktop.in | ||||
| data/gnome-shell-extension-prefs.desktop | ||||
| data/gnome-shell-extension-prefs.desktop.in | ||||
| data/gnome-shell-theme.gresource | ||||
|   | ||||
							
								
								
									
										294
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						| @@ -1,3 +1,297 @@ | ||||
| 3.19.4 | ||||
| ====== | ||||
| * gdm: Do not allow bypassing disabled Sign In button [Michael; #746180] | ||||
| * Style week numbers in calendar [Jakub; #683245] | ||||
| * Misc. bug fixes [Christophe, Jakub, Rui; #759708, #760577, #760945] | ||||
|  | ||||
| Contributors: | ||||
|   Michael Catanzaro, Marek Černocký, Christophe Fergeau, Rui Matos, | ||||
|   Jakub Steiner | ||||
|  | ||||
| Translations: | ||||
|   Aurimas Černius [lt], Enrico Nicoletto [pt_BR], Andika Triwidada [id], | ||||
|   Mario Blättermann [de], Marek Černocký [cs], Kjartan Maraas [nb], | ||||
|   Muhammet Kara [tr], Stas Solovey [ru] | ||||
|  | ||||
| 3.19.3 | ||||
| ====== | ||||
| * Fix thumbnail scaling in window switcher on HiDPI [Florian; #758676] | ||||
| * Update animated backgrounds on timezone changes [Florian; #758939] | ||||
| * loginDialog: Update user list on user changes [Michael; #758568] | ||||
| * Fix touch interaction on wayland [Carlos; #756748] | ||||
|  | ||||
| Contributors: | ||||
|   Michael Catanzaro, Carlos Garnacho, Kalev Lember, Florian Müllner | ||||
|  | ||||
| Translations: | ||||
|   Daniel Korostil [uk], Muhammet Kara [tr], Dušan Kazik [sk], | ||||
|   Baurzhan Muftakhidinov [kk], Marek Černocký [cs] | ||||
|  | ||||
| 3.19.2 | ||||
| ====== | ||||
| * Make gnome-shell DBus activatable [Ray; #741666] | ||||
| * Fix browser plugin crash in Firefox [Carlos; #737932, #757940] | ||||
| * Optionally show battery percentage in system status area [Bastien; #735771] | ||||
| * Misc. bug fixes [Kalev, Florian, Bastien; #757418, #757668, #757779, #757816, | ||||
|   #745626, #758220] | ||||
|  | ||||
| Contributors: | ||||
|   Michael Biebl, Michael Catanzaro, Piotr Drąg, Carlos Garcia Campos, | ||||
|   Kalev Lember, Florian Müllner, Bastien Nocera, Ray Strode | ||||
|  | ||||
| Translations: | ||||
|   Pedro Albuquerque [pt], liushuyu [zh_CN], Yosef Or Boczko [he], | ||||
|   Jiri Grönroos [fi], Kjartan Maraas [nb], GNOME Translation Robot [gd], | ||||
|   Daniel Mustieles [es], Marek Černocký [cs], Kristjan SCHMIDT [eo], | ||||
|   Stas Solovey [ru] | ||||
|  | ||||
| 3.19.1 | ||||
| ====== | ||||
| * Respect text-scaling factor under wayland [Owen; #756447] | ||||
| * Show the Bluetooth submenu when there were setup devices [Bastien; #723848] | ||||
| * Misc. bug fixes [Florian, Cosimo, Rui, Ray, Owen, Jakub, Bastien; | ||||
|   #756697, #756714, #756605, #754814, #738942, #756983, #756925, | ||||
|   #757011, #673235, #757150] | ||||
|  | ||||
| Contributors: | ||||
|   Cosimo Cecchi, Rui Matos, Florian Müllner, Bastien Nocera, Jakub Steiner, | ||||
|   Ray Strode, Owen W. Taylor | ||||
|  | ||||
| Translations: | ||||
|   Kjartan Maraas [nb], Khaled Hosny [ar], Balázs Meskó [hu], | ||||
|   Daniel Șerbănescu [ro], Marek Černocký [cs] | ||||
|  | ||||
| 3.18.1 | ||||
| ====== | ||||
| * Fix screen freezes when a notification is pushed [Carlos; #755425] | ||||
| * Fix overzealous ellipsization in system status menu [Adel, Florian; #708472] | ||||
| * Hide app menu when disabled by setting [Florian; #745919] | ||||
| * Fix lightbox effect when animations are disabled [Rui; #755827] | ||||
| * Do not mark hotplug notifications as critical [Florian; #657923] | ||||
| * Fix icons getting cut off in dash [Florian; #745649] | ||||
| * Animate fullscreen/unfullscreen operations [Cosimo; #707248] | ||||
| * Misc. bug fixes [Florian, Owen; #748919, #674799, #754581] | ||||
|  | ||||
| Contributors: | ||||
|   Emmanuele Bassi, Michael Catanzaro, Cosimo Cecchi, Matthias Clasen, | ||||
|   Adel Gadllah, Carlos Garnacho, Ekaterina Gerasimova, Rui Matos, | ||||
|   Florian Müllner, Owen W. Taylor | ||||
|  | ||||
| Translations: | ||||
|   Марко Костић [sr], Милош Поповић [sr@latin], Khaled Hosny [ar], | ||||
|   Trần Ngọc Quân [vi], Petr Kovar [cs], Alexandre Franke [fr], | ||||
|   Fran Dieguez [gl], Anders Jonsson [sv], Piotr Drąg [pl], Dušan Kazik [sk], | ||||
|   Milo Casagrande [it], Changwoo Ryu [ko], Stas Solovey [ru], | ||||
|   Rafael Fontenelle [pt_BR], Tom Tryfonidis [el], Aurimas Černius [lt], | ||||
|   Seán de Búrca [ga], Christian Kirbach [de], Jiri Grönroos [fi], | ||||
|   Pedro Albuquerque [pt], Baurzhan Muftakhidinov [kk], Daniel Mustieles [es], | ||||
|   Marek Černocký [cs], Ask Hjorth Larsen [da], Inaki Larranaga Murgoitio [eu] | ||||
|  | ||||
| 3.18.0 | ||||
| ====== | ||||
|  | ||||
| Translations: | ||||
|   Sendy Aditya Suryana [id], Kris Thomsen [da], Seán de Búrca [ga], | ||||
|   Andika Triwidada [id], Enrico Nicoletto [pt_BR], Anders Jonsson [sv], | ||||
|   Rūdolfs Mazurs [lv] | ||||
|  | ||||
| 3.17.92 | ||||
| ======= | ||||
| * Fix race when loading multiple background animations [Josselin; #741453] | ||||
|  | ||||
| Contributors: | ||||
|   Michael Biebl, Josselin Mouette, Florian Müllner | ||||
|  | ||||
| Translations: | ||||
|   Baurzhan Muftakhidinov [kk], Changwoo Ryu [ko], Christian Kirbach [de], | ||||
|   Kjartan Maraas [nb], Jiri Grönroos [fi], Arash Mousavi [fa], | ||||
|   Jiro Matsuzawa [ja], Marek Černocký [cs], Milo Casagrande [it] | ||||
|  | ||||
| 3.17.91 | ||||
| ======= | ||||
| * Fix login screen spinner causing wakeups while VT-switched away | ||||
|   [Ray, Rui; #753891] | ||||
| * Fix scrolling of user list on login screen [Florian; #754525] | ||||
|  | ||||
| Contributors: | ||||
|   Piotr Drąg, Rui Matos, Florian Müllner, Ray Strode | ||||
|  | ||||
| Translations: | ||||
|   Dušan Kazik [sk], Jordi Mas [ca], Aurimas Černius [lt], Stas Solovey [ru], | ||||
|   Piotr Drąg [pl], Pedro Albuquerque [pt], Daniel Mustieles [es], | ||||
|   Chao-Hsiung Liao [zh_TW], Muhammet Kara [tr], Fran Dieguez [gl], | ||||
|   Hannie Dumoleyn [nl], Yosef Or Boczko [he], Tom Tryfonidis [el], | ||||
|   A S Alam [pa], Balázs Úr [hu], Alexandre Franke [fr], Frédéric Péters [fr] | ||||
|  | ||||
| 3.17.90 | ||||
| ======= | ||||
| * Avoid caret/focus viewport changes during pointer movement [Rui; #752138] | ||||
| * Match GTK+'s modal dialogs for system modal dialogs [Carlos; #746108] | ||||
| * Refine message list style [Florian; #749958] | ||||
| * Fix type-ahead behavior for backspace and compose key [Rui; #753319, #753320] | ||||
| * Refine the system status menu [Florian; #751377] | ||||
| * Misc. bug fixes and cleanups [Bastien, Ray, Florian, Jakub; #752779, #752739, | ||||
|   #741366, #651503, #753064, #753181, #752881] | ||||
|  | ||||
| Contributors: | ||||
|   Rui Matos, Florian Müllner, Bastien Nocera, Carlos Soriano, Jakub Steiner, | ||||
|   Ray Strode, Rico Tzschichholz | ||||
|  | ||||
| Translations: | ||||
|   Marek Černocký [cs], Kjartan Maraas [nb], Jordi Mas [ca], Muhammet Kara [tr], | ||||
|   Enrico Nicoletto [pt_BR] | ||||
|  | ||||
| 3.17.4 | ||||
| ====== | ||||
| * Fix fuzziness of app menu icon [Jakub; #747932] | ||||
| * Implement 4 finger swipe gesture for touchpads [Carlos; #752250] | ||||
| * Misc. bug fixes [Florian, Alexandre, Piotr, Ray, Mario; #751921, #659969, | ||||
|   #752438, #752675] | ||||
|  | ||||
| Contributors: | ||||
|   Piotr Drąg, Alexandre Franke, Carlos Garnacho, Florian Müllner, | ||||
|   Mario Sanchez Prada, Jakub Steiner, Jasper St. Pierre, Ray Strode | ||||
|  | ||||
| Translations: | ||||
|   Benjamin Steinwender [de], Pedro Albuquerque [pt], Fabio Tomat [fur], | ||||
|   Matej Urbančič [sl], Daniel Mustieles [es], Yosef Or Boczko [he], | ||||
|   Daniel Martinez [an] | ||||
|  | ||||
| 3.17.3 | ||||
| ====== | ||||
| * Handle touch events in OSK on wayland [Rui; #750287] | ||||
| * Reinstate left/right movement to window menu [Ron; #751344] | ||||
| * Allow extensions to disable "Window is ready" notification [Adel; #748846] | ||||
| * Misc. bug fixes [Watson, Michael, Ray, Rui, Florian, Cosimo; #750465, | ||||
|   #751016, #751517, #750714, #751541, #751599] | ||||
|  | ||||
| Contributors: | ||||
|   Michael Biebl, Cosimo Cecchi, Adel Gadllah, Rui Matos, Florian Müllner, | ||||
|   Ray Strode, Wim Taymans, Ron Yorston, Watson Yuuma Sato | ||||
|  | ||||
| Translations: | ||||
|   Sebastian Rasmussen [sv], Dimitris Spingos [el], Muhammet Kara [tr], | ||||
|   Stas Solovey [ru], Benjamin Steinwender [de], Balázs Úr [hu], | ||||
|   Victor Ibragimov [tg], Dušan Kazik [sk], Pedro Albuquerque [pt] | ||||
|  | ||||
| 3.17.2 | ||||
| ====== | ||||
| * Remove StTable widget [Florian; #703833] | ||||
| * Increase visibility of expanders in alt-tab popup [Jakub; #745058] | ||||
| * Ensure suspend inhibitors are released when VT switched away [Rui; #749228] | ||||
| * Use iio-sensor-proxy directly for orientation lock [Bastien; #749671] | ||||
| * Misc. bug fixes [Florian, Lan, Carlos; #749279, #749383, #749529, #749490, | ||||
|   #749742] | ||||
|  | ||||
| Contributors: | ||||
|   Carlos Garnacho, Ting-Wei Lan, Rui Matos, Florian Müllner, Bastien Nocera, | ||||
|   Jakub Steiner | ||||
|  | ||||
| Translations: | ||||
|   Yosef Or Boczko [he], sun [zh_CN], Felipe Braga [pt_BR], | ||||
|   Victor Ibragimov [tg], Gábor Kelemen [hu], Cédric Valmary [oc], | ||||
|   Dušan Kazik [sk], Kjartan Maraas [nb], Bruno Ramalhete [pt], | ||||
|   Matej Urbančič [sl], Daniel Mustieles [es] | ||||
|  | ||||
| 3.17.1 | ||||
| ====== | ||||
| * Add Display Settings entry to background menu [Meet; #697346] | ||||
| * Add window menu option to move to different monitor [Isaac; #633994] | ||||
| * Improve switch style in default/highContrast themes [Jakub; #746294, #747912] | ||||
| * Make event highlight in calendar more prominent [Jakub; #747715] | ||||
| * Fix keyboard focus when focusing a notification banner [Florian; #747205] | ||||
| * Move notification banners below the dateMenu [Meet, Florian; #745910] | ||||
| * Misc. bug fixes [Mario, Rui; #748338, #748541] | ||||
|  | ||||
| Contributors: | ||||
|   Isaac Ge, Rui Matos, Florian Müllner, Meet Parikh, Mario Sanchez Prada, | ||||
|   Jakub Steiner, Jasper St. Pierre | ||||
|  | ||||
| Translations: | ||||
|   Sveinn í Felli [is], Marek Černocký [cs], laurent Soleil [oc] | ||||
|  | ||||
| 3.16.1 | ||||
| ====== | ||||
| * gdm: Move long session chooser menus to the side [Florian; #734352] | ||||
| * Work around background corruption with NVIDIA driver [Rui; #739178] | ||||
| * Don't allow move-to-workspace for always-sticky windows [Florian; #746782] | ||||
| * Allow switching workspaces with PgUp/PgDown in overview [Devyani; #742581] | ||||
| * Bump time PAM messages are displayed [Sarvjeet; #720885] | ||||
| * Fix "stutter" when moving window past the last workspace [Shivam; #712778] | ||||
| * Fix blurred text on login screen [Clément; #746912] | ||||
| * keyboard: Restore whole MRU list after password mode [Rui; #746605] | ||||
| * Pass event timestamps when activating remote actions [Owen; #747323] | ||||
| * Fix hung login screen when password is typed too quickly [Shivam; #737586] | ||||
| * Make on-screen keyboard work for shell chrome on wayland [Rui; #747274] | ||||
| * Implement reexec_self() for FreeBSD [Ting-Wei; #747788] | ||||
| * Allow to dismiss resident notifications [Florian; #746860] | ||||
| * Temporarily reveal legacy tray when icons are added [Florian; #746025] | ||||
| * Make concealed tray smaller to minimize overlap with apps [Florian; #746787] | ||||
| * Misc. bug fixes [Florian, Rui, Giovanni; #746323, #746579, #746902, #746364, | ||||
|   #746509, #747636] | ||||
|  | ||||
| Contributors: | ||||
|   Sarvjeet, Giovanni Campagna, Adel Gadllah, Clément Guérin, Devyani Kota, | ||||
|   Ting-Wei Lan, Rui Matos, Shivam Mishra, Florian Müllner, Owen W. Taylor | ||||
|  | ||||
| Translations: | ||||
|   Khaled Hosny [ar], Dušan Kazik [sk], Yuri Myasoedov [ru], Stas Solovey [ru], | ||||
|   Hannie Dumoleyn [nl], Rūdolfs Mazurs [lv] | ||||
|  | ||||
| 3.16.0 | ||||
| ====== | ||||
| * Revert erroneous login dialog changes [Ray; #740142] | ||||
| * Improve accessibility of legacy tray [Florian; #746487] | ||||
| * Fix legacy status icons leaking into other monitors [Florian; #745824] | ||||
|  | ||||
| Contributors: | ||||
|   Florian Müllner, Ray Strode | ||||
|  | ||||
| Translations: | ||||
|   Daniel Martinez [an], Sebastian Rasmussen [sv], Fran Dieguez [gl], | ||||
|   Andika Triwidada [id], Jordi Mas [ca], Kjartan Maraas [nb], | ||||
|   Inaki Larranaga Murgoitio [eu], Muhammet Kara [tr], Khaled Hosny [ar], | ||||
|   Bernd Homuth [de], Jiro Matsuzawa [ja] | ||||
|  | ||||
| 3.15.92 | ||||
| ======= | ||||
| * gdm: Fix user list accessibility [Florian; #729603] | ||||
| * Handle multiline questions in mount operations [Ross; #745713] | ||||
| * Improve classic theme [Jakub; #745686, #745687] | ||||
| * Fix ordering of calendar events [Florian; #745988] | ||||
| * Pick first input source for new windows when per-window [Rui; #746037] | ||||
| * networkAgent: Show a notification for non-user-initiated password requests | ||||
|   [Giovanni; #660293] | ||||
| * Fix dismissing calendar events [Florian; #744927] | ||||
| * Add legacy tray to ctrl-alt-tab popup [Florian; #746022] | ||||
| * Manage on-screen-keyboard visibility in gnome-shell [Carlos; #745977] | ||||
| * Add pointer barriers to legacy tray [Cosimo; #746026] | ||||
| * Use fallback when app icon cannot be resolved [Cosimo; #746219] | ||||
| * Fix handling of removed smartcard at startup [Ray; #740143] | ||||
| * gdm: Don't pick a random session for the user [Jasper; #740142] | ||||
| * Make menu selection behavior consistent with GTK [Florian; #745246] | ||||
| * gdm: Fix empty user list on user switching [Ray; #719418] | ||||
| * Misc bug fixes [Florian, Giovanni, Clément, Rui; #745666, #746019, #745861, | ||||
|   #746027, #746223, #737502, #746343, #746288] | ||||
|  | ||||
| Contributors: | ||||
|   Giovanni Campagna, Cosimo Cecchi, Piotr Drąg, Adel Gadllah, Carlos Garnacho, | ||||
|   Clément Guérin, Ross Lagerwall, Rui Matos, Florian Müllner, Jakub Steiner, | ||||
|   Jasper St. Pierre, Ray Strode | ||||
|  | ||||
| Translations: | ||||
|   Piotr Drąg [pl], Changwoo Ryu [ko], Milo Casagrande [it], | ||||
|   Baurzhan Muftakhidinov [kk], Мирослав Николић [sr, sr@latin], Balázs Úr [hu], | ||||
|   IWAI, Masaharu [ja], Daniel Korostil [uk], Aurimas Černius [lt], | ||||
|   Matej Urbančič [sl], Daniel Mustieles [es], Kjartan Maraas [nb], | ||||
|   Victor Ibragimov [tg], Claude Paroz [fr], Jordi Mas [ca], Bernd Homuth [de], | ||||
|   Muhammet Kara [tr], Frédéric Péters [fr], Jiri Grönroos [fi], | ||||
|   Alexander Shopov [bg], Stas Solovey [ru], Trần Ngọc Quân [vi], | ||||
|   Samir Ribic [bs], Dušan Kazik [sk], Enrico Nicoletto [pt_BR], | ||||
|   Marek Černocký [cs], A S Alam [pa], Ask Hjorth Larsen [da], | ||||
|   Tom Tryfonidis [el], Alexandre Franke [fr], Yosef Or Boczko [he], | ||||
|   Chao-Hsiung Liao [zh_TW] | ||||
|  | ||||
| 3.15.91 | ||||
| ======= | ||||
| * Don't disable all shortcuts while non-panel menus are open [Florian; #745039] | ||||
|   | ||||
| @@ -3,7 +3,10 @@ mozillalibdir = $(BROWSER_PLUGIN_DIR) | ||||
|  | ||||
| mozillalib_LTLIBRARIES = libgnome-shell-browser-plugin.la | ||||
|  | ||||
| libgnome_shell_browser_plugin_la_LDFLAGS = -module -avoid-version -no-undefined | ||||
| # Browsers can unload and reload the module while browsing, which is not supported by GObject. | ||||
| # We pass -Wl,-z,nodelete to the linker to ensure the module is never unloaded. | ||||
| # https://bugzilla.gnome.org/show_bug.cgi?id=737932 | ||||
| libgnome_shell_browser_plugin_la_LDFLAGS = -module -avoid-version -no-undefined -Wl,-z,nodelete | ||||
|  | ||||
| libgnome_shell_browser_plugin_la_LIBADD = 	\ | ||||
| 	$(BROWSER_PLUGIN_LIBS) | ||||
|   | ||||
| @@ -33,20 +33,16 @@ | ||||
| #include <json-glib/json-glib.h> | ||||
|  | ||||
| #define ORIGIN "extensions.gnome.org" | ||||
| #define PLUGIN_NAME "Gnome Shell Integration" | ||||
| #define PLUGIN_DESCRIPTION "This plugin provides integration with Gnome Shell " \ | ||||
| #define PLUGIN_NAME "GNOME Shell Integration" | ||||
| #define PLUGIN_DESCRIPTION "This plugin provides integration with GNOME Shell " \ | ||||
|       "for live extension enabling and disabling. " \ | ||||
|       "It can be used only by extensions.gnome.org" | ||||
| #define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::Gnome Shell Integration Dummy Content-Type"; | ||||
| #define PLUGIN_MIME_STRING "application/x-gnome-shell-integration::GNOME Shell Integration Dummy Content-Type"; | ||||
|  | ||||
| #define PLUGIN_API_VERSION 5 | ||||
|  | ||||
| #define EXTENSION_DISABLE_VERSION_CHECK_KEY "disable-extension-version-validation" | ||||
|  | ||||
| typedef struct { | ||||
|   GDBusProxy *proxy; | ||||
| } PluginData; | ||||
|  | ||||
| static NPNetscapeFuncs funcs; | ||||
|  | ||||
| static inline gchar * | ||||
| @@ -145,121 +141,6 @@ check_origin_and_protocol (NPP instance) | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| /* =============== public entry points =================== */ | ||||
|  | ||||
| NPError | ||||
| NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin) | ||||
| { | ||||
|   /* global initialization routine, called once when plugin | ||||
|      is loaded */ | ||||
|  | ||||
|   g_debug ("plugin loaded"); | ||||
|  | ||||
|   memcpy (&funcs, pfuncs, sizeof (funcs)); | ||||
|  | ||||
|   plugin->size = sizeof(NPPluginFuncs); | ||||
|   plugin->newp = NPP_New; | ||||
|   plugin->destroy = NPP_Destroy; | ||||
|   plugin->getvalue = NPP_GetValue; | ||||
|   plugin->setwindow = NPP_SetWindow; | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NP_Shutdown(void) | ||||
| { | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| const char* | ||||
| NP_GetMIMEDescription(void) | ||||
| { | ||||
|   return PLUGIN_MIME_STRING; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NP_GetValue(void         *instance, | ||||
|             NPPVariable   variable, | ||||
|             void         *value) | ||||
| { | ||||
|   switch (variable) { | ||||
|   case NPPVpluginNameString: | ||||
|     *(char**)value = PLUGIN_NAME; | ||||
|     break; | ||||
|   case NPPVpluginDescriptionString: | ||||
|     *(char**)value = PLUGIN_DESCRIPTION; | ||||
|     break; | ||||
|   default: | ||||
|     ; | ||||
|   } | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NPP_New(NPMIMEType    mimetype, | ||||
|         NPP           instance, | ||||
|         uint16_t      mode, | ||||
|         int16_t       argc, | ||||
|         char        **argn, | ||||
|         char        **argv, | ||||
|         NPSavedData  *saved) | ||||
| { | ||||
|   /* instance initialization function */ | ||||
|   PluginData *data; | ||||
|   GError *error = NULL; | ||||
|  | ||||
|   g_debug ("plugin created"); | ||||
|  | ||||
|   if (!check_origin_and_protocol (instance)) | ||||
|     return NPERR_GENERIC_ERROR; | ||||
|  | ||||
|   data = g_slice_new (PluginData); | ||||
|   instance->pdata = data; | ||||
|  | ||||
|   data->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, | ||||
|                                                G_DBUS_PROXY_FLAGS_NONE, | ||||
|                                                NULL, /* interface info */ | ||||
|                                                "org.gnome.Shell", | ||||
|                                                "/org/gnome/Shell", | ||||
|                                                "org.gnome.Shell.Extensions", | ||||
|                                                NULL, /* GCancellable */ | ||||
|                                                &error); | ||||
|   if (!data->proxy) | ||||
|     { | ||||
|       /* ignore error if the shell is not running, otherwise warn */ | ||||
|       if (error->domain != G_DBUS_ERROR || | ||||
|           error->code != G_DBUS_ERROR_NAME_HAS_NO_OWNER) | ||||
|         { | ||||
|           g_warning ("Failed to set up Shell proxy: %s", error->message); | ||||
|         } | ||||
|       g_clear_error (&error); | ||||
|       return NPERR_GENERIC_ERROR; | ||||
|     } | ||||
|  | ||||
|   g_debug ("plugin created successfully"); | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NPP_Destroy(NPP           instance, | ||||
| 	    NPSavedData **saved) | ||||
| { | ||||
|   /* instance finalization function */ | ||||
|  | ||||
|   PluginData *data = instance->pdata; | ||||
|  | ||||
|   g_debug ("plugin destroyed"); | ||||
|  | ||||
|   g_object_unref (data->proxy); | ||||
|  | ||||
|   g_slice_free (PluginData, data); | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| /* =================== scripting interface =================== */ | ||||
|  | ||||
| typedef struct { | ||||
| @@ -330,45 +211,18 @@ static NPObject * | ||||
| plugin_object_allocate (NPP      instance, | ||||
|                         NPClass *klass) | ||||
| { | ||||
|   PluginData *data = instance->pdata; | ||||
|   PluginObject *obj = g_slice_new0 (PluginObject); | ||||
|   PluginObject *obj = (PluginObject *) funcs.memalloc (sizeof (PluginObject)); | ||||
|  | ||||
|   memset (obj, 0, sizeof (PluginObject)); | ||||
|   obj->instance = instance; | ||||
|   obj->proxy = g_object_ref (data->proxy); | ||||
|   obj->settings = g_settings_new (SHELL_SCHEMA); | ||||
|   obj->signal_id = g_signal_connect (obj->proxy, "g-signal", | ||||
|                                      G_CALLBACK (on_shell_signal), obj); | ||||
|  | ||||
|   obj->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SESSION, | ||||
|                                          "org.gnome.Shell", | ||||
|                                          G_BUS_NAME_WATCHER_FLAGS_NONE, | ||||
|                                          on_shell_appeared, | ||||
|                                          NULL, | ||||
|                                          obj, | ||||
|                                          NULL); | ||||
|  | ||||
|   g_debug ("plugin object created"); | ||||
|  | ||||
|   return (NPObject*)obj; | ||||
|   return (NPObject*) obj; | ||||
| } | ||||
|  | ||||
| static void | ||||
| plugin_object_deallocate (NPObject *npobj) | ||||
| { | ||||
|   PluginObject *obj = (PluginObject*)npobj; | ||||
|  | ||||
|   g_signal_handler_disconnect (obj->proxy, obj->signal_id); | ||||
|   g_object_unref (obj->proxy); | ||||
|  | ||||
|   if (obj->listener) | ||||
|     funcs.releaseobject (obj->listener); | ||||
|  | ||||
|   if (obj->watch_name_id) | ||||
|     g_bus_unwatch_name (obj->watch_name_id); | ||||
|  | ||||
|   g_debug ("plugin object destroyed"); | ||||
|  | ||||
|   g_slice_free (PluginObject, obj); | ||||
|   funcs.memfree (npobj); | ||||
| } | ||||
|  | ||||
| static inline gboolean | ||||
| @@ -1019,6 +873,149 @@ init_methods_and_properties (void) | ||||
|   onextension_changed_id = funcs.getstringidentifier ("onchange"); | ||||
| } | ||||
|  | ||||
| /* =============== public entry points =================== */ | ||||
|  | ||||
| NPError | ||||
| NP_Initialize(NPNetscapeFuncs *pfuncs, NPPluginFuncs *plugin) | ||||
| { | ||||
|   /* global initialization routine, called once when plugin | ||||
|      is loaded */ | ||||
|  | ||||
|   g_debug ("plugin loaded"); | ||||
|  | ||||
|   memcpy (&funcs, pfuncs, sizeof (funcs)); | ||||
|  | ||||
|   plugin->size = sizeof(NPPluginFuncs); | ||||
|   plugin->newp = NPP_New; | ||||
|   plugin->destroy = NPP_Destroy; | ||||
|   plugin->getvalue = NPP_GetValue; | ||||
|   plugin->setwindow = NPP_SetWindow; | ||||
|   plugin->event = NPP_HandleEvent; | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NP_Shutdown(void) | ||||
| { | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| const char* | ||||
| NP_GetMIMEDescription(void) | ||||
| { | ||||
|   return PLUGIN_MIME_STRING; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NP_GetValue(void         *instance, | ||||
|             NPPVariable   variable, | ||||
|             void         *value) | ||||
| { | ||||
|   switch (variable) { | ||||
|   case NPPVpluginNameString: | ||||
|     *(char**)value = PLUGIN_NAME; | ||||
|     break; | ||||
|   case NPPVpluginDescriptionString: | ||||
|     *(char**)value = PLUGIN_DESCRIPTION; | ||||
|     break; | ||||
|   default: | ||||
|     ; | ||||
|   } | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NPP_New(NPMIMEType    mimetype, | ||||
|         NPP           instance, | ||||
|         uint16_t      mode, | ||||
|         int16_t       argc, | ||||
|         char        **argn, | ||||
|         char        **argv, | ||||
|         NPSavedData  *saved) | ||||
| { | ||||
|   /* instance initialization function */ | ||||
|   PluginObject *obj; | ||||
|   GError *error = NULL; | ||||
|  | ||||
|   g_debug ("plugin created"); | ||||
|  | ||||
|   if (!check_origin_and_protocol (instance)) | ||||
|     return NPERR_GENERIC_ERROR; | ||||
|  | ||||
|   /* set windowless mode */ | ||||
|   funcs.setvalue(instance, NPPVpluginWindowBool, NULL); | ||||
|  | ||||
|   g_debug ("creating scriptable object"); | ||||
|   init_methods_and_properties (); | ||||
|   obj = (PluginObject *) funcs.createobject (instance, &plugin_class); | ||||
|   instance->pdata = obj; | ||||
|  | ||||
|   obj->proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SESSION, | ||||
|                                               G_DBUS_PROXY_FLAGS_NONE, | ||||
|                                               NULL, /* interface info */ | ||||
|                                               "org.gnome.Shell", | ||||
|                                               "/org/gnome/Shell", | ||||
|                                               "org.gnome.Shell.Extensions", | ||||
|                                               NULL, /* GCancellable */ | ||||
|                                               &error); | ||||
|   if (!obj->proxy) | ||||
|     { | ||||
|       /* ignore error if the shell is not running, otherwise warn */ | ||||
|       if (!g_error_matches (error, G_DBUS_ERROR, G_DBUS_ERROR_NAME_HAS_NO_OWNER)) | ||||
|         { | ||||
|           g_warning ("Failed to set up Shell proxy: %s", error->message); | ||||
|         } | ||||
|       g_clear_error (&error); | ||||
|       return NPERR_GENERIC_ERROR; | ||||
|     } | ||||
|  | ||||
|   obj->settings = g_settings_new (SHELL_SCHEMA); | ||||
|   obj->signal_id = g_signal_connect (obj->proxy, "g-signal", | ||||
|                                      G_CALLBACK (on_shell_signal), obj); | ||||
|   obj->watch_name_id = g_bus_watch_name (G_BUS_TYPE_SESSION, | ||||
|                                          "org.gnome.Shell", | ||||
|                                          G_BUS_NAME_WATCHER_FLAGS_NONE, | ||||
|                                          on_shell_appeared, | ||||
|                                          NULL, | ||||
|                                          obj, | ||||
|                                          NULL); | ||||
|  | ||||
|   g_debug ("plugin created successfully"); | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NPP_Destroy(NPP           instance, | ||||
| 	    NPSavedData **saved) | ||||
| { | ||||
|   /* instance finalization function */ | ||||
|   PluginObject *obj = (PluginObject *) instance->pdata; | ||||
|  | ||||
|   if (!obj) | ||||
|     return NPERR_INVALID_INSTANCE_ERROR; | ||||
|  | ||||
|   g_debug ("plugin destroyed"); | ||||
|  | ||||
|   g_signal_handler_disconnect (obj->proxy, obj->signal_id); | ||||
|   g_object_unref (obj->proxy); | ||||
|  | ||||
|   if (obj->listener) | ||||
|     funcs.releaseobject (obj->listener); | ||||
|  | ||||
|   if (obj->restart_listener) | ||||
|     funcs.releaseobject (obj->restart_listener); | ||||
|  | ||||
|   if (obj->watch_name_id) | ||||
|     g_bus_unwatch_name (obj->watch_name_id); | ||||
|  | ||||
|   funcs.releaseobject((NPObject *)obj); | ||||
|  | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| NPError | ||||
| NPP_GetValue(NPP          instance, | ||||
| 	     NPPVariable  variable, | ||||
| @@ -1029,13 +1026,10 @@ NPP_GetValue(NPP          instance, | ||||
|   switch (variable) { | ||||
|   case NPPVpluginScriptableNPObject: | ||||
|     g_debug ("creating scriptable object"); | ||||
|     init_methods_and_properties (); | ||||
|     if (!instance->pdata) | ||||
|       return NPERR_INVALID_INSTANCE_ERROR; | ||||
|  | ||||
|     *(NPObject**)value = funcs.createobject (instance, &plugin_class); | ||||
|     break; | ||||
|  | ||||
|   case NPPVpluginNeedsXEmbed: | ||||
|     *(bool *)value = TRUE; | ||||
|     *(NPObject**)value = instance->pdata; | ||||
|     break; | ||||
|  | ||||
|   default: | ||||
| @@ -1053,3 +1047,11 @@ NPP_SetWindow(NPP          instance, | ||||
| { | ||||
|   return NPERR_NO_ERROR; | ||||
| } | ||||
|  | ||||
| int16_t | ||||
| NPP_HandleEvent(NPP   instance, | ||||
|                 void *event) | ||||
| { | ||||
|   /* Ignore the event */ | ||||
|   return FALSE; | ||||
| } | ||||
|   | ||||
							
								
								
									
										25
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						| @@ -1,5 +1,6 @@ | ||||
| AC_PREREQ(2.63) | ||||
| AC_INIT([gnome-shell],[3.15.91],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) | ||||
| AC_INIT([gnome-shell],[3.19.4],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) | ||||
| AX_IS_RELEASE([git-directory]) | ||||
|  | ||||
| AC_CONFIG_HEADERS([config.h]) | ||||
| AC_CONFIG_SRCDIR([src/shell-global.c]) | ||||
| @@ -63,8 +64,8 @@ AC_ARG_ENABLE([systemd], | ||||
|               [enable_systemd=$enableval], | ||||
|               [enable_systemd=auto]) | ||||
| AS_IF([test x$enable_systemd != xno], [ | ||||
|   AC_MSG_CHECKING([for libsystemd-journal]) | ||||
|   PKG_CHECK_EXISTS([libsystemd-journal], | ||||
|   AC_MSG_CHECKING([for libsystemd]) | ||||
|   PKG_CHECK_EXISTS([libsystemd], | ||||
|                    [have_systemd=yes | ||||
|                     AC_DEFINE([HAVE_SYSTEMD], [1], [Define if we have systemd])], | ||||
|                    [have_systemd=no]) | ||||
| @@ -74,13 +75,13 @@ AS_IF([test x$enable_systemd != xno], [ | ||||
| AC_MSG_RESULT($enable_systemd) | ||||
|  | ||||
| CLUTTER_MIN_VERSION=1.21.5 | ||||
| GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1 | ||||
| GOBJECT_INTROSPECTION_MIN_VERSION=1.45.4 | ||||
| GJS_MIN_VERSION=1.39.0 | ||||
| MUTTER_MIN_VERSION=3.15.91 | ||||
| MUTTER_MIN_VERSION=3.19.4 | ||||
| GTK_MIN_VERSION=3.15.0 | ||||
| GIO_MIN_VERSION=2.37.0 | ||||
| GIO_MIN_VERSION=2.45.3 | ||||
| LIBECAL_MIN_VERSION=3.5.3 | ||||
| LIBEDATASERVER_MIN_VERSION=3.13.90 | ||||
| LIBEDATASERVER_MIN_VERSION=3.17.2 | ||||
| TELEPATHY_GLIB_MIN_VERSION=0.17.5 | ||||
| POLKIT_MIN_VERSION=0.100 | ||||
| STARTUP_NOTIFICATION_MIN_VERSION=0.11 | ||||
| @@ -106,7 +107,7 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION | ||||
|             polkit-agent-1 >= $POLKIT_MIN_VERSION | ||||
|             gcr-base-3 >= $GCR_MIN_VERSION" | ||||
| if test x$have_systemd = xyes; then | ||||
|   SHARED_PCS="${SHARED_PCS} libsystemd-journal" | ||||
|   SHARED_PCS="${SHARED_PCS} libsystemd" | ||||
| fi | ||||
|  | ||||
| PKG_CHECK_MODULES(GNOME_SHELL, $SHARED_PCS) | ||||
| @@ -116,9 +117,9 @@ 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(TRAY, gtk+-3.0) | ||||
| PKG_CHECK_MODULES(TRAY, clutter-1.0 gtk+-3.0) | ||||
| PKG_CHECK_MODULES(GVC, libpulse >= $PULSE_MIN_VERS libpulse-mainloop-glib gobject-2.0) | ||||
| PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.13.1) | ||||
| PKG_CHECK_MODULES(DESKTOP_SCHEMAS, gsettings-desktop-schemas >= 3.19.2) | ||||
|  | ||||
| AC_ARG_ENABLE(browser-plugin, | ||||
|               [AS_HELP_STRING([--enable-browser-plugin], | ||||
| @@ -220,7 +221,7 @@ if test "$enable_man" != no; then | ||||
| fi | ||||
| AM_CONDITIONAL(ENABLE_MAN, test "$enable_man" != no) | ||||
|  | ||||
| GNOME_COMPILE_WARNINGS([error]) | ||||
| AX_COMPILER_FLAGS() | ||||
| case "$WARN_CFLAGS" in | ||||
|     *-Werror*) | ||||
|         WARN_CFLAGS="$WARN_CFLAGS -Wno-error=deprecated-declarations" | ||||
| @@ -269,7 +270,7 @@ Build configuration: | ||||
|        Prefix:                                 ${prefix} | ||||
|        Source code location:                   ${srcdir} | ||||
|        Compiler:                               ${CC} | ||||
|        Compiler Warnings:                      $enable_compile_warnings | ||||
|        Compiler Warnings:                      $ax_enable_compile_warnings | ||||
|  | ||||
|        Support for NetworkManager:             $have_networkmanager | ||||
|        Support for GStreamer recording:        $build_recorder | ||||
|   | ||||
| @@ -2,7 +2,7 @@ CLEANFILES = | ||||
| NULL = | ||||
|  | ||||
| desktopdir=$(datadir)/applications | ||||
| desktop_DATA = gnome-shell.desktop gnome-shell-wayland.desktop  gnome-shell-extension-prefs.desktop | ||||
| desktop_DATA = org.gnome.Shell.desktop gnome-shell-extension-prefs.desktop | ||||
|  | ||||
| if HAVE_NETWORKMANAGER | ||||
| desktop_DATA += org.gnome.Shell.PortalHelper.desktop | ||||
| @@ -104,8 +104,7 @@ convertdir = $(datadir)/GConf/gsettings | ||||
| convert_DATA = gnome-shell-overrides.convert | ||||
|  | ||||
| EXTRA_DIST =						\ | ||||
| 	gnome-shell.desktop.in.in			\ | ||||
| 	gnome-shell-wayland.desktop.in.in		\ | ||||
| 	org.gnome.Shell.desktop.in.in			\ | ||||
| 	gnome-shell-extension-prefs.desktop.in.in	\ | ||||
| 	$(introspection_DATA)				\ | ||||
| 	$(menu_DATA)					\ | ||||
| @@ -121,8 +120,7 @@ EXTRA_DIST =						\ | ||||
| 	$(NULL) | ||||
|  | ||||
| CLEANFILES +=						\ | ||||
| 	gnome-shell.desktop.in				\ | ||||
| 	gnome-shell-wayland.desktop.in			\ | ||||
| 	org.gnome.Shell.desktop.in			\ | ||||
| 	gnome-shell-extension-prefs.in			\ | ||||
| 	$(desktop_DATA)					\ | ||||
| 	$(keys_DATA)					\ | ||||
|   | ||||
| @@ -32,8 +32,10 @@ | ||||
|     <file>summary-counter.svg</file> | ||||
|     <file>toggle-off-us.svg</file> | ||||
|     <file>toggle-off-intl.svg</file> | ||||
|     <file>toggle-off-hc.svg</file> | ||||
|     <file>toggle-on-us.svg</file> | ||||
|     <file>toggle-on-intl.svg</file> | ||||
|     <file>toggle-on-hc.svg</file> | ||||
|     <file>ws-switch-arrow-up.png</file> | ||||
|     <file>ws-switch-arrow-down.png</file> | ||||
|   </gresource> | ||||
|   | ||||
| @@ -1,15 +0,0 @@ | ||||
| [Desktop Entry] | ||||
| Type=Application | ||||
| _Name=GNOME Shell (wayland compositor) | ||||
| _Comment=Window management and application launching | ||||
| Exec=@bindir@/gnome-shell --wayland --display-server | ||||
| X-GNOME-Bugzilla-Bugzilla=GNOME | ||||
| X-GNOME-Bugzilla-Product=gnome-shell | ||||
| X-GNOME-Bugzilla-Component=general | ||||
| X-GNOME-Bugzilla-Version=@VERSION@ | ||||
| Categories=GNOME;GTK;Core; | ||||
| OnlyShowIn=GNOME; | ||||
| NoDisplay=true | ||||
| X-GNOME-Autostart-Phase=DisplayServer | ||||
| X-GNOME-Autostart-Notify=true | ||||
| X-GNOME-AutoRestart=false | ||||
| @@ -10,7 +10,7 @@ X-GNOME-Bugzilla-Version=@VERSION@ | ||||
| Categories=GNOME;GTK;Core; | ||||
| OnlyShowIn=GNOME; | ||||
| NoDisplay=true | ||||
| X-GNOME-Autostart-Phase=WindowManager | ||||
| X-GNOME-Autostart-Phase=DisplayServer | ||||
| X-GNOME-Provides=panel;windowmanager; | ||||
| X-GNOME-Autostart-Notify=true | ||||
| X-GNOME-AutoRestart=false | ||||
| @@ -51,6 +51,7 @@ | ||||
|     </key> | ||||
|     <key name="looking-glass-history" type="as"> | ||||
|       <default>[]</default> | ||||
|       <!-- Translators: looking glass is a debugger and inspector tool, see https://live.gnome.org/GnomeShell/LookingGlass --> | ||||
|       <_summary>History for the looking glass dialog</_summary> | ||||
|     </key> | ||||
|     <key name="always-show-log-out" type="b"> | ||||
| @@ -71,6 +72,16 @@ | ||||
|         This key sets the default state of the checkbox. | ||||
|       </_description> | ||||
|     </key> | ||||
|     <key name="had-bluetooth-devices-setup" type="b"> | ||||
|       <default>false</default> | ||||
|       <_summary>Whether the default Bluetooth adapter had set up devices associated to it</_summary> | ||||
|       <_description> | ||||
|         The shell will only show a Bluetooth menu item if a Bluetooth | ||||
|         adapter is powered, or if there were devices set up associated | ||||
|         with the default adapter. This will be reset if the default | ||||
|         adapter is ever seen not to have devices associated to it. | ||||
|       </_description> | ||||
|     </key> | ||||
|     <child name="calendar" schema="org.gnome.shell.calendar"/> | ||||
|     <child name="keybindings" schema="org.gnome.shell.keybindings"/> | ||||
|     <child name="keyboard" schema="org.gnome.shell.keyboard"/> | ||||
|   | ||||
| @@ -10,11 +10,11 @@ | ||||
|    xmlns:xlink="http://www.w3.org/1999/xlink" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    width="29" | ||||
|    height="29" | ||||
|    width="24" | ||||
|    height="24" | ||||
|    id="svg10621" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.48.2 r9819" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="calendar-today.svg"> | ||||
|   <defs | ||||
|      id="defs10623"> | ||||
| @@ -118,17 +118,6 @@ | ||||
|        fx="51" | ||||
|        fy="30" | ||||
|        r="42" /> | ||||
|     <radialGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient34508-1-3" | ||||
|        id="radialGradient3113" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(0.72146227,0,0,0.27484277,14.205424,21.754717)" | ||||
|        cx="51" | ||||
|        cy="30" | ||||
|        fx="51" | ||||
|        fy="30" | ||||
|        r="42" /> | ||||
|   </defs> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
| @@ -137,22 +126,23 @@ | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="15.839192" | ||||
|      inkscape:cx="20.652108" | ||||
|      inkscape:cy="11.839084" | ||||
|      inkscape:zoom="8" | ||||
|      inkscape:cx="-23.537329" | ||||
|      inkscape:cy="-31.442864" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="layer1" | ||||
|      showgrid="true" | ||||
|      showgrid="false" | ||||
|      fit-margin-top="0" | ||||
|      fit-margin-left="0" | ||||
|      fit-margin-right="0" | ||||
|      fit-margin-bottom="0" | ||||
|      inkscape:window-width="1280" | ||||
|      inkscape:window-height="741" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
|      borderlayer="true"> | ||||
|      inkscape:window-width="2133" | ||||
|      inkscape:window-height="1241" | ||||
|      inkscape:window-x="238" | ||||
|      inkscape:window-y="88" | ||||
|      inkscape:window-maximized="0" | ||||
|      borderlayer="true" | ||||
|      inkscape:showpageshadow="false"> | ||||
|     <inkscape:grid | ||||
|        type="xygrid" | ||||
|        id="grid3109" | ||||
| @@ -169,7 +159,7 @@ | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <dc:title /> | ||||
|         <dc:title></dc:title> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
| @@ -177,28 +167,12 @@ | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1" | ||||
|      transform="translate(-469.08263,-532.99307)"> | ||||
|     <path | ||||
|        sodipodi:type="arc" | ||||
|        style="opacity:0.4625;color:#000000;fill:url(#radialGradient3113);fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" | ||||
|        id="path34506-3" | ||||
|        sodipodi:cx="51" | ||||
|        sodipodi:cy="30" | ||||
|        sodipodi:rx="42" | ||||
|        sodipodi:ry="16" | ||||
|        d="M 9,29.999999 A 42,16 0 0 1 93,30 l -42,0 z" | ||||
|        sodipodi:start="3.1415927" | ||||
|        sodipodi:end="6.2831853" | ||||
|        transform="matrix(0.43692393,0,0,1.3783114,461.29951,517.6437)" | ||||
|        inkscape:export-filename="/home/jimmac/src/cvs/gnome/gnome-shell-design/mockups/motion/textures/panel.png" | ||||
|        inkscape:export-xdpi="90" | ||||
|        inkscape:export-ydpi="90" /> | ||||
|     <rect | ||||
|        style="fill:#ffffff;fill-opacity:0.50196078;stroke-width:0.43599999;stroke-miterlimit:4;stroke-dasharray:none" | ||||
|        id="rect2996" | ||||
|        width="31" | ||||
|        height="3" | ||||
|        x="468.08264" | ||||
|        y="558.99304" /> | ||||
|      transform="translate(-469.08263,-537.99307)"> | ||||
|     <circle | ||||
|        style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:0.23756906;fill-rule:nonzero;stroke:none;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|        id="path7305" | ||||
|        cx="481.57138" | ||||
|        cy="559.4649" | ||||
|        r="1.5" /> | ||||
|   </g> | ||||
| </svg> | ||||
|   | ||||
| Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 5.6 KiB | 
| @@ -37,14 +37,13 @@ stage { | ||||
|   icon-shadow: 0 1px black; } | ||||
|   .button:focus { | ||||
|     color: #eeeeec; | ||||
|     border-color: #215d9c; | ||||
|     box-shadow: inset 0 1px #454f52; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; } | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .button:insensitive { | ||||
|     color: #7f7f7f; | ||||
|     color: gray; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: rgba(62, 67, 68, 0.7); | ||||
|     background-color: rgba(62, 67, 69, 0.7); | ||||
|     box-shadow: none; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
| @@ -52,9 +51,46 @@ stage { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|  | ||||
| .modal-dialog-linked-button { | ||||
|   border-right-width: 1px; | ||||
|   color: #eeeeec; | ||||
|   background-color: #2e3436; | ||||
|   border-color: rgba(0, 0, 0, 0.7); | ||||
|   box-shadow: inset 0 1px #454f52; | ||||
|   text-shadow: 0 1px black; | ||||
|   icon-shadow: 0 1px black; | ||||
|   padding: 12px; } | ||||
|   .modal-dialog-linked-button:insensitive { | ||||
|     color: gray; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: rgba(62, 67, 69, 0.7); | ||||
|     box-shadow: none; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .modal-dialog-linked-button:active { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .modal-dialog-linked-button:focus { | ||||
|     color: #eeeeec; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .modal-dialog-linked-button:first-child { | ||||
|     border-radius: 0px 0px 0px 6px; } | ||||
|   .modal-dialog-linked-button:last-child { | ||||
|     border-right-width: 0px; | ||||
|     border-radius: 0px 0px 6px 0px; } | ||||
|   .modal-dialog-linked-button:first-child:last-child { | ||||
|     border-right-width: 0px; | ||||
|     border-radius: 0px 0px 6px 6px; } | ||||
|  | ||||
| /* Entries */ | ||||
| StEntry { | ||||
| @@ -71,8 +107,8 @@ StEntry { | ||||
|     box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); | ||||
|     border-color: rgba(166, 166, 166, 0.5); } | ||||
|   StEntry:insensitive { | ||||
|     color: #7f7f7f; | ||||
|     border-color: #0d0d0d; | ||||
|     color: gray; | ||||
|     border-color: #0e0e0e; | ||||
|     box-shadow: none; } | ||||
|   StEntry StIcon.capslock-warning { | ||||
|     icon-size: 16px; | ||||
| @@ -80,12 +116,13 @@ StEntry { | ||||
|     padding: 0 4px; } | ||||
|  | ||||
| /* Scrollbars */ | ||||
| StScrollView.vfade { | ||||
|   -st-vfade-offset: 68px; } | ||||
| StScrollView.hfade { | ||||
|   -st-hfade-offset: 68px; } | ||||
|  | ||||
| StScrollBar { | ||||
|   padding: 0; } | ||||
|   StScrollBar.vfade { | ||||
|     -st-vfade-offset: 68px; } | ||||
|   StScrollBar.hfade { | ||||
|     -st-hfade-offset: 68px; } | ||||
|   StScrollView StScrollBar { | ||||
|     min-width: 14px; | ||||
|     min-height: 14px; } | ||||
| @@ -94,10 +131,10 @@ StScrollBar { | ||||
|     background-color: transparent; } | ||||
|   StScrollBar StButton#vhandle, StScrollBar StButton#hhandle { | ||||
|     border-radius: 8px; | ||||
|     background-color: #000; | ||||
|     background-color: #999999; | ||||
|     margin: 3px; } | ||||
|     StScrollBar StButton#vhandle:hover, StScrollBar StButton#hhandle:hover { | ||||
|       background-color: #1a1a1a; } | ||||
|       background-color: #cccccc; } | ||||
|     StScrollBar StButton#vhandle:active, StScrollBar StButton#hhandle:active { | ||||
|       background-color: #215d9c; } | ||||
|  | ||||
| @@ -105,7 +142,7 @@ StScrollBar { | ||||
| .slider { | ||||
|   height: 1em; | ||||
|   -slider-height: 0.3em; | ||||
|   -slider-background-color: #0d0d0d; | ||||
|   -slider-background-color: #0e0e0e; | ||||
|   -slider-border-color: black; | ||||
|   -slider-active-background-color: #215d9c; | ||||
|   -slider-active-border-color: #184472; | ||||
| @@ -159,11 +196,12 @@ StScrollBar { | ||||
|   background-color: white; } | ||||
|  | ||||
| .modal-dialog { | ||||
|   border-radius: 5px; | ||||
|   border-radius: 9px; | ||||
|   color: #eeeeec; | ||||
|   background-color: rgba(23, 25, 26, 0.95); | ||||
|   border: 3px solid rgba(238, 238, 236, 0.5); | ||||
|   padding: 24px; } | ||||
|   border: 1px solid rgba(238, 238, 236, 0.2); } | ||||
|   .modal-dialog .modal-dialog-content-box { | ||||
|     padding: 24px; } | ||||
|   .modal-dialog .run-dialog-entry { | ||||
|     width: 20em; | ||||
|     margin-bottom: 6px; } | ||||
| @@ -178,10 +216,6 @@ StScrollBar { | ||||
|     color: #d6d6d1; | ||||
|     padding-bottom: .4em; } | ||||
|  | ||||
| .button-dialog-button-box { | ||||
|   spacing: 18px; | ||||
|   padding-top: 48px; } | ||||
|  | ||||
| .show-processes-dialog-subject, | ||||
| .mount-question-dialog-subject, | ||||
| .end-session-dialog-subject { | ||||
| @@ -190,7 +224,7 @@ StScrollBar { | ||||
| /* End Session Dialog */ | ||||
| .end-session-dialog { | ||||
|   spacing: 42px; | ||||
|   border: 3px solid rgba(238, 238, 236, 0.2); } | ||||
|   border: 1px solid rgba(238, 238, 236, 0.2); } | ||||
|  | ||||
| .end-session-dialog-list { | ||||
|   padding-top: 20px; } | ||||
| @@ -323,7 +357,7 @@ StScrollBar { | ||||
|  | ||||
| .prompt-dialog-headline { | ||||
|   font-weight: bold; | ||||
|   color: #b3b3b3; } | ||||
|   color: #b2b2a9; } | ||||
|  | ||||
| .prompt-dialog-description:rtl { | ||||
|   text-align: right; } | ||||
| @@ -374,9 +408,9 @@ StScrollBar { | ||||
|   spacing-rows: 15px; | ||||
|   spacing-columns: 1em; } | ||||
|  | ||||
| /* Popvers/Menus */ | ||||
| /* Popovers/Menus */ | ||||
| .popup-menu { | ||||
|   min-width: 200px; } | ||||
|   min-width: 15em; } | ||||
|   .popup-menu .popup-sub-menu { | ||||
|     background-color: black; | ||||
|     box-shadow: inset 0 -1px 0px #0d0d0d; } | ||||
| @@ -392,19 +426,14 @@ StScrollBar { | ||||
|       background-color: black; | ||||
|       box-shadow: inset 0 1px 0px #0d0d0d; | ||||
|       font-weight: bold; } | ||||
|     .popup-menu .popup-menu-item:focus { | ||||
|       background-color: #215d9c; | ||||
|       color: #ffffff; } | ||||
|     .popup-menu .popup-menu-item:hover { | ||||
|     .popup-menu .popup-menu-item.selected { | ||||
|       background-color: rgba(255, 255, 255, 0.1); | ||||
|       color: #fff; } | ||||
|     .popup-menu .popup-menu-item:active { | ||||
|       background-color: #1c5187; | ||||
|       background-color: #215d9c; | ||||
|       color: #ffffff; } | ||||
|     .popup-menu .popup-menu-item:insensitive { | ||||
|       color: rgba(255, 255, 255, 0.5); } | ||||
|   .popup-menu .active { | ||||
|     background-color: #215d9c; } | ||||
|   .popup-menu .popup-inactive-menu-item { | ||||
|     color: #fff; } | ||||
|     .popup-menu .popup-inactive-menu-item:insensitive { | ||||
| @@ -415,7 +444,7 @@ StScrollBar { | ||||
|  | ||||
| .popup-menu-ornament { | ||||
|   text-align: right; | ||||
|   width: 1em; } | ||||
|   width: 1.2em; } | ||||
|  | ||||
| .popup-menu-boxpointer, | ||||
| .candidate-popup-boxpointer { | ||||
| @@ -492,10 +521,9 @@ StScrollBar { | ||||
|  | ||||
| .switcher-arrow { | ||||
|   border-color: transparent; | ||||
|   color: black; } | ||||
|  | ||||
| .switcher-arrow:highlighted { | ||||
|   color: #fff; } | ||||
|   color: rgba(255, 255, 255, 0.8); } | ||||
|   .switcher-arrow:highlighted { | ||||
|     color: #fff; } | ||||
|  | ||||
| .input-source-switcher-symbol { | ||||
|   font-size: 34pt; | ||||
| @@ -565,7 +593,7 @@ StScrollBar { | ||||
|     -panel-corner-border-color: transparent; } | ||||
|     #panel .panel-corner:active, #panel .panel-corner:overview, #panel .panel-corner:focus { | ||||
|       -panel-corner-border-color: #256ab1; } | ||||
|     #panel .panel-corner.lock-screen, #panel .panel-corner.login-screen, #panel .panel-cornerunlock-screen { | ||||
|     #panel .panel-corner.lock-screen, #panel .panel-corner.login-screen, #panel .panel-corner.unlock-screen { | ||||
|       -panel-corner-radius: 0; | ||||
|       -panel-corner-background-color: transparent; | ||||
|       -panel-corner-border-color: transparent; } | ||||
| @@ -597,6 +625,8 @@ StScrollBar { | ||||
|   #panel .panel-status-indicators-box, | ||||
|   #panel .panel-status-menu-box { | ||||
|     spacing: 2px; } | ||||
|   #panel .power-status.panel-status-indicators-box { | ||||
|     spacing: 0; } | ||||
|   #panel .screencast-indicator { | ||||
|     color: #f57900; } | ||||
|  | ||||
| @@ -707,23 +737,35 @@ StScrollBar { | ||||
|   border-left-width: 1px; } | ||||
|  | ||||
| .calendar-nonwork-day { | ||||
|   color: #7f7f7f; } | ||||
|   color: gray; } | ||||
|  | ||||
| .calendar-today { | ||||
|   font-weight: bold; | ||||
|   border: 1px solid rgba(0, 0, 0, 0.5); } | ||||
|  | ||||
| .calendar-day-with-events { | ||||
|   color: #f2f2f2; | ||||
|   font-weight: bold; } | ||||
|   color: white; | ||||
|   font-weight: bold; | ||||
|   background-image: url("resource:///org/gnome/shell/theme/calendar-today.svg"); } | ||||
|  | ||||
| .calendar-other-month-day { | ||||
|   color: rgba(255, 255, 255, 0.15); | ||||
|   opacity: 0.5; } | ||||
|  | ||||
| .calendar-week-number { | ||||
|   font-size: 70%; | ||||
|   font-weight: bold; | ||||
|   width: 2.8em; | ||||
|   height: 2em; | ||||
|   border-radius: 2px 1em 2px 2px; | ||||
|   padding: 0.9em 0 0; | ||||
|   margin: 3px; | ||||
|   background-color: rgba(255, 255, 255, 0.1); | ||||
|   color: #000; } | ||||
|  | ||||
| /* Message list */ | ||||
| .message-list { | ||||
|   width: 420px; } | ||||
|   width: 31.5em; } | ||||
|  | ||||
| .message-list-sections { | ||||
|   spacing: 1.5em; } | ||||
| @@ -758,7 +800,12 @@ StScrollBar { | ||||
|     padding: 8px 8px 8px 0px; } | ||||
|  | ||||
| .message-icon-bin > StIcon { | ||||
|   icon-size: 48px; } | ||||
|   icon-size: 32px; } | ||||
|  | ||||
| .message-secondary-bin:ltr { | ||||
|   padding-left: 8px; } | ||||
| .message-secondary-bin:rtl { | ||||
|   padding-right: 8px; } | ||||
|  | ||||
| .message-secondary-bin { | ||||
|   color: #999999; } | ||||
| @@ -767,14 +814,20 @@ StScrollBar { | ||||
|   icon-size: 16px; } | ||||
|  | ||||
| .message-title { | ||||
|   font-weight: bold; } | ||||
|   font-weight: bold; | ||||
|   font-size: 1.1em; } | ||||
|  | ||||
| .message-content { | ||||
|   padding: 8px; } | ||||
|   padding: 8px; | ||||
|   font-size: .9em; } | ||||
|  | ||||
| .system-switch-user-submenu-icon { | ||||
|   icon-size: 24px; | ||||
|   border: 1px solid rgba(255, 255, 255, 0.4); } | ||||
| .system-switch-user-submenu-icon.user-icon { | ||||
|   icon-size: 20px; | ||||
|   padding: 0 2px; } | ||||
|  | ||||
| .system-switch-user-submenu-icon.default-icon { | ||||
|   icon-size: 16px; | ||||
|   padding: 0 4px; } | ||||
|  | ||||
| #appMenu { | ||||
|   spinner-image: url("resource:///org/gnome/shell/theme/process-working.svg"); | ||||
| @@ -783,7 +836,7 @@ StScrollBar { | ||||
|     color: transparent; } | ||||
|  | ||||
| .aggregate-menu { | ||||
|   width: 360px; } | ||||
|   min-width: 21em; } | ||||
|   .aggregate-menu .popup-menu-icon { | ||||
|     padding: 0 4px; } | ||||
|  | ||||
| @@ -794,12 +847,12 @@ StScrollBar { | ||||
|   padding: 13px; | ||||
|   border: 1px solid #0d0d0d; } | ||||
|   .system-menu-action:hover, .system-menu-action:focus { | ||||
|     color: #ffffff; | ||||
|     background-color: #215d9c; | ||||
|     background-color: rgba(255, 255, 255, 0.1); | ||||
|     color: #fff; | ||||
|     border: none; | ||||
|     padding: 14px; } | ||||
|   .system-menu-action:active { | ||||
|     background-color: #1c5187; | ||||
|     background-color: #215d9c; | ||||
|     color: #ffffff; } | ||||
|   .system-menu-action > StIcon { | ||||
|     icon-size: 16px; } | ||||
| @@ -820,7 +873,7 @@ StScrollBar { | ||||
| .popup-menu-icon { | ||||
|   icon-size: 1.09em; } | ||||
|  | ||||
| .window-close, .notification-close { | ||||
| .window-close { | ||||
|   background-image: url("resource:///org/gnome/shell/theme/close-window.svg"); | ||||
|   background-size: 32px; | ||||
|   height: 32px; | ||||
| @@ -831,12 +884,6 @@ StScrollBar { | ||||
|   .window-close:rtl { | ||||
|     -st-background-image-shadow: 2px 2px 6px rgba(0, 0, 0, 0.5); } | ||||
|  | ||||
| .notification-close { | ||||
|   -shell-close-overlap-x: 14px; | ||||
|   -shell-close-overlap-y: -12px; } | ||||
|   .notification-close:rtl { | ||||
|     -shell-close-overlap-x: -14px; } | ||||
|  | ||||
| /* NETWORK DIALOGS */ | ||||
| .nm-dialog { | ||||
|   max-height: 500px; | ||||
| @@ -844,7 +891,8 @@ StScrollBar { | ||||
|   min-width: 470px; } | ||||
|  | ||||
| .nm-dialog-content { | ||||
|   spacing: 20px; } | ||||
|   spacing: 20px; | ||||
|   padding: 24px; } | ||||
|  | ||||
| .nm-dialog-header-hbox { | ||||
|   spacing: 10px; } | ||||
| @@ -925,10 +973,14 @@ StScrollBar { | ||||
| .search-entry { | ||||
|   width: 320px; | ||||
|   padding: 7px 9px; | ||||
|   border-radius: 6px; } | ||||
|   border-radius: 6px; | ||||
|   border-color: #747467; | ||||
|   color: #eeeeec; | ||||
|   background-color: #2e3436; } | ||||
|   .search-entry:focus { | ||||
|     padding: 6px 8px; | ||||
|     border-width: 2px; } | ||||
|     border-width: 2px; | ||||
|     border-color: #215d9c; } | ||||
|   .search-entry .search-entry-icon { | ||||
|     icon-size: 1em; | ||||
|     padding: 0 4px; | ||||
| @@ -1026,7 +1078,7 @@ StScrollBar { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: none; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .app-view-control:first-child { | ||||
| @@ -1157,7 +1209,7 @@ StScrollBar { | ||||
|  | ||||
| /* NOTIFICATIONS & MESSAGE TRAY */ | ||||
| .url-highlighter { | ||||
|   link-color: #215d9c; } | ||||
|   link-color: #2a76c6; } | ||||
|  | ||||
| .notification-banner { | ||||
|   font-size: 11pt; | ||||
| @@ -1192,32 +1244,6 @@ StScrollBar { | ||||
|     .notification-banner .notification-button:hover, .notification-banner .notification-buttonfocus { | ||||
|       background-color: #292f30; } | ||||
|  | ||||
| .notification { | ||||
|   font-size: 11pt; | ||||
|   width: 34em; | ||||
|   margin: 5px; | ||||
|   border-radius: 6px; | ||||
|   color: #eeeeec; | ||||
|   background-color: #2e3436; | ||||
|   border: 1px solid black; | ||||
|   spacing-rows: 4px; | ||||
|   padding: 8px; | ||||
|   spacing-columns: 10px; } | ||||
|  | ||||
| .notification-unexpanded { | ||||
|   min-height: 48px; | ||||
|   height: 48px; } | ||||
|  | ||||
| .notification-with-image { | ||||
|   min-height: 159px; } | ||||
|  | ||||
| .notification-body { | ||||
|   spacing: 5px; } | ||||
|  | ||||
| .notification-actions { | ||||
|   paddinf-top: 18px; | ||||
|   spacing: 6px; } | ||||
|  | ||||
| .summary-source-counter { | ||||
|   font-size: 10pt; | ||||
|   font-weight: bold; | ||||
| @@ -1231,36 +1257,20 @@ StScrollBar { | ||||
|   box-shadow: 0 2px 2px rgba(0, 0, 0, 0.5); | ||||
|   border-radius: 0.9em; } | ||||
|  | ||||
| .notification-scrollview { | ||||
|   max-height: 18em; | ||||
|   -st-vfade-offset: 24px; } | ||||
|   .notification-scrollview:ltr > StScrollBar { | ||||
|     padding-left: 6px; } | ||||
|   .notification-scrollview:rtl > StScrollBar { | ||||
|     padding-right: 6px; } | ||||
|  | ||||
| .notification-button { | ||||
|   height: 24px; } | ||||
|  | ||||
| .notification-icon-button { | ||||
|   border-radius: 5px; | ||||
|   padding: 5px; | ||||
|   height: 24px; | ||||
|   width: 24px; } | ||||
|   .notification-icon-button > StIcon { | ||||
|     icons-size: 16px; | ||||
|     width: 16px; | ||||
|     height: 16px; | ||||
|     padding: 2px; } | ||||
|  | ||||
| .secondary-icon { | ||||
|   icon-size: 1.09em; } | ||||
|  | ||||
| .chat-body { | ||||
|   spacing: 5px; } | ||||
|  | ||||
| .chat-response { | ||||
|   margin: 5px; } | ||||
|  | ||||
| .chat-log-message { | ||||
|   color: #e6e6e6; } | ||||
|  | ||||
| .chat-empty-line { | ||||
|   font-size: 4px; } | ||||
| .chat-new-group { | ||||
|   padding-top: 1em; } | ||||
|  | ||||
| .chat-received { | ||||
|   padding-left: 4px; } | ||||
| @@ -1284,12 +1294,6 @@ StScrollBar { | ||||
|     padding-left: 0; | ||||
|     padding-right: 4px; } | ||||
|  | ||||
| .chat-notification-scrollview { | ||||
|   max-height: 22em; } | ||||
|  | ||||
| .subscription-message { | ||||
|   font-style: italic; } | ||||
|  | ||||
| .hotplug-transient-box { | ||||
|   spacing: 6px; | ||||
|   padding: 2px 72px 2px 12px; } | ||||
| @@ -1340,12 +1344,23 @@ StScrollBar { | ||||
|     border-radius: 6px 0 0 0; | ||||
|     border-right-width: 0; } | ||||
|  | ||||
| .legacy-tray-handle StIcon { | ||||
|   icon-size: 24px; } | ||||
| .legacy-tray-handle, | ||||
| .legacy-tray-icon { | ||||
|   padding: 6px; } | ||||
|   .legacy-tray-handle StIcon, | ||||
|   .legacy-tray-icon StIcon { | ||||
|     icon-size: 24px; } | ||||
|   .legacy-tray-handle:hover, .legacy-tray-handle:focus, | ||||
|   .legacy-tray-icon:hover, | ||||
|   .legacy-tray-icon:focus { | ||||
|     background-color: rgba(255, 255, 255, 0.1); } | ||||
|  | ||||
| .legacy-tray-icon-box { | ||||
|   padding: 6px; | ||||
|   spacing: 12px; } | ||||
|   .legacy-tray-icon-box:ltr { | ||||
|     padding-left: 12px; } | ||||
|   .legacy-tray-icon-box:rtl { | ||||
|     padding-right: 12px; } | ||||
|   .legacy-tray-icon-box StButton { | ||||
|     width: 24px; | ||||
|     height: 24px; } | ||||
| @@ -1382,10 +1397,9 @@ StScrollBar { | ||||
|   color: white; } | ||||
|   .keyboard-key:focus { | ||||
|     color: #eeeeec; | ||||
|     border-color: #215d9c; | ||||
|     box-shadow: inset 0 1px #454f52; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; } | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .keyboard-key:hover, .keyboard-key:checked { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
| @@ -1397,7 +1411,7 @@ StScrollBar { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: none; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .keyboard-key:grayed { | ||||
| @@ -1454,10 +1468,12 @@ StScrollBar { | ||||
| /* Auth Dialogs & Screen Shield */ | ||||
| .framed-user-icon { | ||||
|   background-size: contain; | ||||
|   border: 2px solid black; | ||||
|   border: 2px solid #eeeeec; | ||||
|   color: #eeeeec; | ||||
|   border-radius: 3px; } | ||||
|   .framed-user-icon:hover { | ||||
|     border-color: #4d4d4d; } | ||||
|     border-color: white; | ||||
|     color: white; } | ||||
|  | ||||
| .login-dialog-banner-view { | ||||
|   padding-top: 24px; | ||||
| @@ -1488,13 +1504,13 @@ StScrollBar { | ||||
|         color: white; | ||||
|         border-color: rgba(0, 0, 0, 0.7); | ||||
|         background-color: #1c5187; | ||||
|         box-shadow: none; | ||||
|         box-shadow: inset 0 0 black; | ||||
|         text-shadow: none; | ||||
|         icon-shadow: none; } | ||||
|       .login-dialog .modal-dialog-button:default:insensitive { | ||||
|         color: #7f7f7f; | ||||
|         color: gray; | ||||
|         border-color: rgba(0, 0, 0, 0.7); | ||||
|         background-color: rgba(62, 67, 68, 0.7); | ||||
|         background-color: rgba(62, 67, 69, 0.7); | ||||
|         box-shadow: none; | ||||
|         text-shadow: none; | ||||
|         icon-shadow: none; } | ||||
| @@ -1554,9 +1570,10 @@ StScrollBar { | ||||
|     color: #ffffff; } | ||||
|   .login-dialog-user-list-item .login-dialog-timed-login-indicator { | ||||
|     height: 2px; | ||||
|     background-color: transparent; } | ||||
|     margin: 2px 0 0 0; | ||||
|     background-color: #eeeeec; } | ||||
|   .login-dialog-user-list-item:focus .login-dialog-timed-login-indicator { | ||||
|     background-color: #2e3436; } | ||||
|     background-color: #ffffff; } | ||||
|  | ||||
| .login-dialog-username, | ||||
| .user-widget-label { | ||||
| @@ -1740,3 +1757,11 @@ StScrollBar { | ||||
|  | ||||
| stage { | ||||
|   -st-icon-style: symbolic; } | ||||
|  | ||||
| .toggle-switch { | ||||
|   width: 48px; } | ||||
|  | ||||
| .toggle-switch-us, .toggle-switch-intl { | ||||
|   background-image: url("resource:///org/gnome/shell/theme/toggle-off-hc.svg"); } | ||||
|   .toggle-switch-us:checked, .toggle-switch-intl:checked { | ||||
|     background-image: url("resource:///org/gnome/shell/theme/toggle-on-hc.svg"); } | ||||
|   | ||||
| @@ -6,3 +6,9 @@ | ||||
| stage { | ||||
|   -st-icon-style: symbolic; | ||||
| } | ||||
|  | ||||
| .toggle-switch { width: 48px; } | ||||
| .toggle-switch-us, .toggle-switch-intl { | ||||
|   background-image: url("resource:///org/gnome/shell/theme/toggle-off-hc.svg"); | ||||
|   &:checked { background-image: url("resource:///org/gnome/shell/theme/toggle-on-hc.svg"); } | ||||
| } | ||||
|   | ||||
| @@ -37,14 +37,13 @@ stage { | ||||
|   icon-shadow: 0 1px black; } | ||||
|   .button:focus { | ||||
|     color: #eeeeec; | ||||
|     border-color: #215d9c; | ||||
|     box-shadow: inset 0 1px #454f52; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; } | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .button:insensitive { | ||||
|     color: #939695; | ||||
|     color: #949796; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: rgba(66, 71, 73, 0.7); | ||||
|     background-color: rgba(66, 72, 73, 0.7); | ||||
|     box-shadow: none; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
| @@ -52,9 +51,46 @@ stage { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|  | ||||
| .modal-dialog-linked-button { | ||||
|   border-right-width: 1px; | ||||
|   color: #eeeeec; | ||||
|   background-color: #2e3436; | ||||
|   border-color: rgba(0, 0, 0, 0.7); | ||||
|   box-shadow: inset 0 1px #454f52; | ||||
|   text-shadow: 0 1px black; | ||||
|   icon-shadow: 0 1px black; | ||||
|   padding: 12px; } | ||||
|   .modal-dialog-linked-button:insensitive { | ||||
|     color: #949796; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: rgba(66, 72, 73, 0.7); | ||||
|     box-shadow: none; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .modal-dialog-linked-button:active { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .modal-dialog-linked-button:focus { | ||||
|     color: #eeeeec; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .modal-dialog-linked-button:first-child { | ||||
|     border-radius: 0px 0px 0px 6px; } | ||||
|   .modal-dialog-linked-button:last-child { | ||||
|     border-right-width: 0px; | ||||
|     border-radius: 0px 0px 6px 0px; } | ||||
|   .modal-dialog-linked-button:first-child:last-child { | ||||
|     border-right-width: 0px; | ||||
|     border-radius: 0px 0px 6px 6px; } | ||||
|  | ||||
| /* Entries */ | ||||
| StEntry { | ||||
| @@ -71,8 +107,8 @@ StEntry { | ||||
|     box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.4); | ||||
|     border-color: rgba(154, 154, 142, 0.5); } | ||||
|   StEntry:insensitive { | ||||
|     color: #939695; | ||||
|     border-color: #323636; | ||||
|     color: #949796; | ||||
|     border-color: #333636; | ||||
|     box-shadow: none; } | ||||
|   StEntry StIcon.capslock-warning { | ||||
|     icon-size: 16px; | ||||
| @@ -80,12 +116,13 @@ StEntry { | ||||
|     padding: 0 4px; } | ||||
|  | ||||
| /* Scrollbars */ | ||||
| StScrollView.vfade { | ||||
|   -st-vfade-offset: 68px; } | ||||
| StScrollView.hfade { | ||||
|   -st-hfade-offset: 68px; } | ||||
|  | ||||
| StScrollBar { | ||||
|   padding: 0; } | ||||
|   StScrollBar.vfade { | ||||
|     -st-vfade-offset: 68px; } | ||||
|   StScrollBar.hfade { | ||||
|     -st-hfade-offset: 68px; } | ||||
|   StScrollView StScrollBar { | ||||
|     min-width: 14px; | ||||
|     min-height: 14px; } | ||||
| @@ -94,10 +131,10 @@ StScrollBar { | ||||
|     background-color: transparent; } | ||||
|   StScrollBar StButton#vhandle, StScrollBar StButton#hhandle { | ||||
|     border-radius: 8px; | ||||
|     background-color: #393f3f; | ||||
|     background-color: #a6a8a7; | ||||
|     margin: 3px; } | ||||
|     StScrollBar StButton#vhandle:hover, StScrollBar StButton#hhandle:hover { | ||||
|       background-color: #515a5a; } | ||||
|       background-color: #cacbc9; } | ||||
|     StScrollBar StButton#vhandle:active, StScrollBar StButton#hhandle:active { | ||||
|       background-color: #215d9c; } | ||||
|  | ||||
| @@ -105,7 +142,7 @@ StScrollBar { | ||||
| .slider { | ||||
|   height: 1em; | ||||
|   -slider-height: 0.3em; | ||||
|   -slider-background-color: #323636; | ||||
|   -slider-background-color: #333636; | ||||
|   -slider-border-color: #1c1f1f; | ||||
|   -slider-active-background-color: #215d9c; | ||||
|   -slider-active-border-color: #184472; | ||||
| @@ -159,11 +196,12 @@ StScrollBar { | ||||
|   background-color: white; } | ||||
|  | ||||
| .modal-dialog { | ||||
|   border-radius: 5px; | ||||
|   border-radius: 9px; | ||||
|   color: #eeeeec; | ||||
|   background-color: rgba(23, 25, 26, 0.95); | ||||
|   border: 3px solid rgba(238, 238, 236, 0.5); | ||||
|   padding: 24px; } | ||||
|   border: 1px solid rgba(238, 238, 236, 0.2); } | ||||
|   .modal-dialog .modal-dialog-content-box { | ||||
|     padding: 24px; } | ||||
|   .modal-dialog .run-dialog-entry { | ||||
|     width: 20em; | ||||
|     margin-bottom: 6px; } | ||||
| @@ -178,10 +216,6 @@ StScrollBar { | ||||
|     color: #d6d6d1; | ||||
|     padding-bottom: .4em; } | ||||
|  | ||||
| .button-dialog-button-box { | ||||
|   spacing: 18px; | ||||
|   padding-top: 48px; } | ||||
|  | ||||
| .show-processes-dialog-subject, | ||||
| .mount-question-dialog-subject, | ||||
| .end-session-dialog-subject { | ||||
| @@ -190,7 +224,7 @@ StScrollBar { | ||||
| /* End Session Dialog */ | ||||
| .end-session-dialog { | ||||
|   spacing: 42px; | ||||
|   border: 3px solid rgba(238, 238, 236, 0.2); } | ||||
|   border: 1px solid rgba(238, 238, 236, 0.2); } | ||||
|  | ||||
| .end-session-dialog-list { | ||||
|   padding-top: 20px; } | ||||
| @@ -323,7 +357,7 @@ StScrollBar { | ||||
|  | ||||
| .prompt-dialog-headline { | ||||
|   font-weight: bold; | ||||
|   color: #a6a69b; } | ||||
|   color: #b2b2a9; } | ||||
|  | ||||
| .prompt-dialog-description:rtl { | ||||
|   text-align: right; } | ||||
| @@ -374,9 +408,9 @@ StScrollBar { | ||||
|   spacing-rows: 15px; | ||||
|   spacing-columns: 1em; } | ||||
|  | ||||
| /* Popvers/Menus */ | ||||
| /* Popovers/Menus */ | ||||
| .popup-menu { | ||||
|   min-width: 200px; } | ||||
|   min-width: 15em; } | ||||
|   .popup-menu .popup-sub-menu { | ||||
|     background-color: #343a3a; | ||||
|     box-shadow: inset 0 -1px 0px #282c2c; } | ||||
| @@ -392,19 +426,14 @@ StScrollBar { | ||||
|       background-color: #343a3a; | ||||
|       box-shadow: inset 0 1px 0px #282c2c; | ||||
|       font-weight: bold; } | ||||
|     .popup-menu .popup-menu-item:focus { | ||||
|       background-color: #215d9c; | ||||
|       color: #ffffff; } | ||||
|     .popup-menu .popup-menu-item:hover { | ||||
|     .popup-menu .popup-menu-item.selected { | ||||
|       background-color: rgba(238, 238, 236, 0.1); | ||||
|       color: #eeeeec; } | ||||
|     .popup-menu .popup-menu-item:active { | ||||
|       background-color: #1c5187; | ||||
|       background-color: #215d9c; | ||||
|       color: #ffffff; } | ||||
|     .popup-menu .popup-menu-item:insensitive { | ||||
|       color: rgba(238, 238, 236, 0.5); } | ||||
|   .popup-menu .active { | ||||
|     background-color: #215d9c; } | ||||
|   .popup-menu .popup-inactive-menu-item { | ||||
|     color: #eeeeec; } | ||||
|     .popup-menu .popup-inactive-menu-item:insensitive { | ||||
| @@ -415,7 +444,7 @@ StScrollBar { | ||||
|  | ||||
| .popup-menu-ornament { | ||||
|   text-align: right; | ||||
|   width: 1em; } | ||||
|   width: 1.2em; } | ||||
|  | ||||
| .popup-menu-boxpointer, | ||||
| .candidate-popup-boxpointer { | ||||
| @@ -492,10 +521,9 @@ StScrollBar { | ||||
|  | ||||
| .switcher-arrow { | ||||
|   border-color: transparent; | ||||
|   color: #1c1f1f; } | ||||
|  | ||||
| .switcher-arrow:highlighted { | ||||
|   color: #eeeeec; } | ||||
|   color: rgba(238, 238, 236, 0.8); } | ||||
|   .switcher-arrow:highlighted { | ||||
|     color: #eeeeec; } | ||||
|  | ||||
| .input-source-switcher-symbol { | ||||
|   font-size: 34pt; | ||||
| @@ -565,7 +593,7 @@ StScrollBar { | ||||
|     -panel-corner-border-color: transparent; } | ||||
|     #panel .panel-corner:active, #panel .panel-corner:overview, #panel .panel-corner:focus { | ||||
|       -panel-corner-border-color: #256ab1; } | ||||
|     #panel .panel-corner.lock-screen, #panel .panel-corner.login-screen, #panel .panel-cornerunlock-screen { | ||||
|     #panel .panel-corner.lock-screen, #panel .panel-corner.login-screen, #panel .panel-corner.unlock-screen { | ||||
|       -panel-corner-radius: 0; | ||||
|       -panel-corner-background-color: transparent; | ||||
|       -panel-corner-border-color: transparent; } | ||||
| @@ -597,6 +625,8 @@ StScrollBar { | ||||
|   #panel .panel-status-indicators-box, | ||||
|   #panel .panel-status-menu-box { | ||||
|     spacing: 2px; } | ||||
|   #panel .power-status.panel-status-indicators-box { | ||||
|     spacing: 0; } | ||||
|   #panel .screencast-indicator { | ||||
|     color: #f57900; } | ||||
|  | ||||
| @@ -707,23 +737,35 @@ StScrollBar { | ||||
|   border-left-width: 1px; } | ||||
|  | ||||
| .calendar-nonwork-day { | ||||
|   color: #939695; } | ||||
|   color: #949796; } | ||||
|  | ||||
| .calendar-today { | ||||
|   font-weight: bold; | ||||
|   border: 1px solid rgba(28, 31, 31, 0.5); } | ||||
|  | ||||
| .calendar-day-with-events { | ||||
|   color: #e2e2df; | ||||
|   font-weight: bold; } | ||||
|   color: white; | ||||
|   font-weight: bold; | ||||
|   background-image: url("resource:///org/gnome/shell/theme/calendar-today.svg"); } | ||||
|  | ||||
| .calendar-other-month-day { | ||||
|   color: rgba(238, 238, 236, 0.15); | ||||
|   opacity: 0.5; } | ||||
|  | ||||
| .calendar-week-number { | ||||
|   font-size: 70%; | ||||
|   font-weight: bold; | ||||
|   width: 2.8em; | ||||
|   height: 2em; | ||||
|   border-radius: 2px 1em 2px 2px; | ||||
|   padding: 0.9em 0 0; | ||||
|   margin: 3px; | ||||
|   background-color: rgba(238, 238, 236, 0.1); | ||||
|   color: #393f3f; } | ||||
|  | ||||
| /* Message list */ | ||||
| .message-list { | ||||
|   width: 420px; } | ||||
|   width: 31.5em; } | ||||
|  | ||||
| .message-list-sections { | ||||
|   spacing: 1.5em; } | ||||
| @@ -758,7 +800,12 @@ StScrollBar { | ||||
|     padding: 8px 8px 8px 0px; } | ||||
|  | ||||
| .message-icon-bin > StIcon { | ||||
|   icon-size: 48px; } | ||||
|   icon-size: 32px; } | ||||
|  | ||||
| .message-secondary-bin:ltr { | ||||
|   padding-left: 8px; } | ||||
| .message-secondary-bin:rtl { | ||||
|   padding-right: 8px; } | ||||
|  | ||||
| .message-secondary-bin { | ||||
|   color: #8e8e80; } | ||||
| @@ -767,14 +814,20 @@ StScrollBar { | ||||
|   icon-size: 16px; } | ||||
|  | ||||
| .message-title { | ||||
|   font-weight: bold; } | ||||
|   font-weight: bold; | ||||
|   font-size: 1.1em; } | ||||
|  | ||||
| .message-content { | ||||
|   padding: 8px; } | ||||
|   padding: 8px; | ||||
|   font-size: .9em; } | ||||
|  | ||||
| .system-switch-user-submenu-icon { | ||||
|   icon-size: 24px; | ||||
|   border: 1px solid rgba(238, 238, 236, 0.4); } | ||||
| .system-switch-user-submenu-icon.user-icon { | ||||
|   icon-size: 20px; | ||||
|   padding: 0 2px; } | ||||
|  | ||||
| .system-switch-user-submenu-icon.default-icon { | ||||
|   icon-size: 16px; | ||||
|   padding: 0 4px; } | ||||
|  | ||||
| #appMenu { | ||||
|   spinner-image: url("resource:///org/gnome/shell/theme/process-working.svg"); | ||||
| @@ -783,7 +836,7 @@ StScrollBar { | ||||
|     color: transparent; } | ||||
|  | ||||
| .aggregate-menu { | ||||
|   width: 360px; } | ||||
|   min-width: 21em; } | ||||
|   .aggregate-menu .popup-menu-icon { | ||||
|     padding: 0 4px; } | ||||
|  | ||||
| @@ -794,12 +847,12 @@ StScrollBar { | ||||
|   padding: 13px; | ||||
|   border: 1px solid #282c2c; } | ||||
|   .system-menu-action:hover, .system-menu-action:focus { | ||||
|     color: #ffffff; | ||||
|     background-color: #215d9c; | ||||
|     background-color: rgba(238, 238, 236, 0.1); | ||||
|     color: #eeeeec; | ||||
|     border: none; | ||||
|     padding: 14px; } | ||||
|   .system-menu-action:active { | ||||
|     background-color: #1c5187; | ||||
|     background-color: #215d9c; | ||||
|     color: #ffffff; } | ||||
|   .system-menu-action > StIcon { | ||||
|     icon-size: 16px; } | ||||
| @@ -820,7 +873,7 @@ StScrollBar { | ||||
| .popup-menu-icon { | ||||
|   icon-size: 1.09em; } | ||||
|  | ||||
| .window-close, .notification-close { | ||||
| .window-close { | ||||
|   background-image: url("resource:///org/gnome/shell/theme/close-window.svg"); | ||||
|   background-size: 32px; | ||||
|   height: 32px; | ||||
| @@ -831,12 +884,6 @@ StScrollBar { | ||||
|   .window-close:rtl { | ||||
|     -st-background-image-shadow: 2px 2px 6px rgba(0, 0, 0, 0.5); } | ||||
|  | ||||
| .notification-close { | ||||
|   -shell-close-overlap-x: 14px; | ||||
|   -shell-close-overlap-y: -12px; } | ||||
|   .notification-close:rtl { | ||||
|     -shell-close-overlap-x: -14px; } | ||||
|  | ||||
| /* NETWORK DIALOGS */ | ||||
| .nm-dialog { | ||||
|   max-height: 500px; | ||||
| @@ -844,7 +891,8 @@ StScrollBar { | ||||
|   min-width: 470px; } | ||||
|  | ||||
| .nm-dialog-content { | ||||
|   spacing: 20px; } | ||||
|   spacing: 20px; | ||||
|   padding: 24px; } | ||||
|  | ||||
| .nm-dialog-header-hbox { | ||||
|   spacing: 10px; } | ||||
| @@ -925,10 +973,14 @@ StScrollBar { | ||||
| .search-entry { | ||||
|   width: 320px; | ||||
|   padding: 7px 9px; | ||||
|   border-radius: 6px; } | ||||
|   border-radius: 6px; | ||||
|   border-color: #747467; | ||||
|   color: #eeeeec; | ||||
|   background-color: #2e3436; } | ||||
|   .search-entry:focus { | ||||
|     padding: 6px 8px; | ||||
|     border-width: 2px; } | ||||
|     border-width: 2px; | ||||
|     border-color: #215d9c; } | ||||
|   .search-entry .search-entry-icon { | ||||
|     icon-size: 1em; | ||||
|     padding: 0 4px; | ||||
| @@ -1026,7 +1078,7 @@ StScrollBar { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: none; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .app-view-control:first-child { | ||||
| @@ -1157,7 +1209,7 @@ StScrollBar { | ||||
|  | ||||
| /* NOTIFICATIONS & MESSAGE TRAY */ | ||||
| .url-highlighter { | ||||
|   link-color: #215d9c; } | ||||
|   link-color: #2a76c6; } | ||||
|  | ||||
| .notification-banner { | ||||
|   font-size: 11pt; | ||||
| @@ -1192,32 +1244,6 @@ StScrollBar { | ||||
|     .notification-banner .notification-button:hover, .notification-banner .notification-buttonfocus { | ||||
|       background-color: #292f30; } | ||||
|  | ||||
| .notification { | ||||
|   font-size: 11pt; | ||||
|   width: 34em; | ||||
|   margin: 5px; | ||||
|   border-radius: 6px; | ||||
|   color: #eeeeec; | ||||
|   background-color: #2e3436; | ||||
|   border: 1px solid #1c1f1f; | ||||
|   spacing-rows: 4px; | ||||
|   padding: 8px; | ||||
|   spacing-columns: 10px; } | ||||
|  | ||||
| .notification-unexpanded { | ||||
|   min-height: 48px; | ||||
|   height: 48px; } | ||||
|  | ||||
| .notification-with-image { | ||||
|   min-height: 159px; } | ||||
|  | ||||
| .notification-body { | ||||
|   spacing: 5px; } | ||||
|  | ||||
| .notification-actions { | ||||
|   paddinf-top: 18px; | ||||
|   spacing: 6px; } | ||||
|  | ||||
| .summary-source-counter { | ||||
|   font-size: 10pt; | ||||
|   font-weight: bold; | ||||
| @@ -1231,36 +1257,20 @@ StScrollBar { | ||||
|   box-shadow: 0 2px 2px rgba(0, 0, 0, 0.5); | ||||
|   border-radius: 0.9em; } | ||||
|  | ||||
| .notification-scrollview { | ||||
|   max-height: 18em; | ||||
|   -st-vfade-offset: 24px; } | ||||
|   .notification-scrollview:ltr > StScrollBar { | ||||
|     padding-left: 6px; } | ||||
|   .notification-scrollview:rtl > StScrollBar { | ||||
|     padding-right: 6px; } | ||||
|  | ||||
| .notification-button { | ||||
|   height: 24px; } | ||||
|  | ||||
| .notification-icon-button { | ||||
|   border-radius: 5px; | ||||
|   padding: 5px; | ||||
|   height: 24px; | ||||
|   width: 24px; } | ||||
|   .notification-icon-button > StIcon { | ||||
|     icons-size: 16px; | ||||
|     width: 16px; | ||||
|     height: 16px; | ||||
|     padding: 2px; } | ||||
|  | ||||
| .secondary-icon { | ||||
|   icon-size: 1.09em; } | ||||
|  | ||||
| .chat-body { | ||||
|   spacing: 5px; } | ||||
|  | ||||
| .chat-response { | ||||
|   margin: 5px; } | ||||
|  | ||||
| .chat-log-message { | ||||
|   color: #d6d6d1; } | ||||
|  | ||||
| .chat-empty-line { | ||||
|   font-size: 4px; } | ||||
| .chat-new-group { | ||||
|   padding-top: 1em; } | ||||
|  | ||||
| .chat-received { | ||||
|   padding-left: 4px; } | ||||
| @@ -1284,12 +1294,6 @@ StScrollBar { | ||||
|     padding-left: 0; | ||||
|     padding-right: 4px; } | ||||
|  | ||||
| .chat-notification-scrollview { | ||||
|   max-height: 22em; } | ||||
|  | ||||
| .subscription-message { | ||||
|   font-style: italic; } | ||||
|  | ||||
| .hotplug-transient-box { | ||||
|   spacing: 6px; | ||||
|   padding: 2px 72px 2px 12px; } | ||||
| @@ -1340,12 +1344,23 @@ StScrollBar { | ||||
|     border-radius: 6px 0 0 0; | ||||
|     border-right-width: 0; } | ||||
|  | ||||
| .legacy-tray-handle StIcon { | ||||
|   icon-size: 24px; } | ||||
| .legacy-tray-handle, | ||||
| .legacy-tray-icon { | ||||
|   padding: 6px; } | ||||
|   .legacy-tray-handle StIcon, | ||||
|   .legacy-tray-icon StIcon { | ||||
|     icon-size: 24px; } | ||||
|   .legacy-tray-handle:hover, .legacy-tray-handle:focus, | ||||
|   .legacy-tray-icon:hover, | ||||
|   .legacy-tray-icon:focus { | ||||
|     background-color: rgba(238, 238, 236, 0.1); } | ||||
|  | ||||
| .legacy-tray-icon-box { | ||||
|   padding: 6px; | ||||
|   spacing: 12px; } | ||||
|   .legacy-tray-icon-box:ltr { | ||||
|     padding-left: 12px; } | ||||
|   .legacy-tray-icon-box:rtl { | ||||
|     padding-right: 12px; } | ||||
|   .legacy-tray-icon-box StButton { | ||||
|     width: 24px; | ||||
|     height: 24px; } | ||||
| @@ -1382,10 +1397,9 @@ StScrollBar { | ||||
|   color: white; } | ||||
|   .keyboard-key:focus { | ||||
|     color: #eeeeec; | ||||
|     border-color: #215d9c; | ||||
|     box-shadow: inset 0 1px #454f52; | ||||
|     text-shadow: 0 1px black; | ||||
|     icon-shadow: 0 1px black; } | ||||
|     icon-shadow: 0 1px black; | ||||
|     box-shadow: inset 0px 0px 0px 1px #215d9c; } | ||||
|   .keyboard-key:hover, .keyboard-key:checked { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
| @@ -1397,7 +1411,7 @@ StScrollBar { | ||||
|     color: white; | ||||
|     border-color: rgba(0, 0, 0, 0.7); | ||||
|     background-color: #222728; | ||||
|     box-shadow: none; | ||||
|     box-shadow: inset 0 0 black; | ||||
|     text-shadow: none; | ||||
|     icon-shadow: none; } | ||||
|   .keyboard-key:grayed { | ||||
| @@ -1454,10 +1468,12 @@ StScrollBar { | ||||
| /* Auth Dialogs & Screen Shield */ | ||||
| .framed-user-icon { | ||||
|   background-size: contain; | ||||
|   border: 2px solid #1c1f1f; | ||||
|   border: 2px solid #eeeeec; | ||||
|   color: #eeeeec; | ||||
|   border-radius: 3px; } | ||||
|   .framed-user-icon:hover { | ||||
|     border-color: #656f6f; } | ||||
|     border-color: white; | ||||
|     color: white; } | ||||
|  | ||||
| .login-dialog-banner-view { | ||||
|   padding-top: 24px; | ||||
| @@ -1488,13 +1504,13 @@ StScrollBar { | ||||
|         color: white; | ||||
|         border-color: rgba(0, 0, 0, 0.7); | ||||
|         background-color: #1c5187; | ||||
|         box-shadow: none; | ||||
|         box-shadow: inset 0 0 black; | ||||
|         text-shadow: none; | ||||
|         icon-shadow: none; } | ||||
|       .login-dialog .modal-dialog-button:default:insensitive { | ||||
|         color: #939695; | ||||
|         color: #949796; | ||||
|         border-color: rgba(0, 0, 0, 0.7); | ||||
|         background-color: rgba(66, 71, 73, 0.7); | ||||
|         background-color: rgba(66, 72, 73, 0.7); | ||||
|         box-shadow: none; | ||||
|         text-shadow: none; | ||||
|         icon-shadow: none; } | ||||
| @@ -1554,9 +1570,10 @@ StScrollBar { | ||||
|     color: #ffffff; } | ||||
|   .login-dialog-user-list-item .login-dialog-timed-login-indicator { | ||||
|     height: 2px; | ||||
|     background-color: transparent; } | ||||
|     margin: 2px 0 0 0; | ||||
|     background-color: #eeeeec; } | ||||
|   .login-dialog-user-list-item:focus .login-dialog-timed-login-indicator { | ||||
|     background-color: #2e3436; } | ||||
|     background-color: #ffffff; } | ||||
|  | ||||
| .login-dialog-username, | ||||
| .user-widget-label { | ||||
|   | ||||
| Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 98 KiB | 
							
								
								
									
										133
									
								
								data/theme/toggle-off-hc.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,133 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    width="48" | ||||
|    height="22" | ||||
|    id="svg2857" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="toggle-off-hc.svg"> | ||||
|   <defs | ||||
|      id="defs2859"> | ||||
|     <inkscape:perspective | ||||
|        sodipodi:type="inkscape:persp3d" | ||||
|        inkscape:vp_x="0 : 526.18109 : 1" | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_z="744.09448 : 526.18109 : 1" | ||||
|        inkscape:persp3d-origin="372.04724 : 350.78739 : 1" | ||||
|        id="perspective2865" /> | ||||
|     <inkscape:perspective | ||||
|        id="perspective2843" | ||||
|        inkscape:persp3d-origin="0.5 : 0.33333333 : 1" | ||||
|        inkscape:vp_z="1 : 0.5 : 1" | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_x="0 : 0.5 : 1" | ||||
|        sodipodi:type="inkscape:persp3d" /> | ||||
|     <inkscape:path-effect | ||||
|        effect="spiro" | ||||
|        id="path-effect77541-4" | ||||
|        is_visible="true" /> | ||||
|   </defs> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
|      pagecolor="#000000" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="1" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="1" | ||||
|      inkscape:cx="-6.1820581" | ||||
|      inkscape:cy="-16.463788" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="g37994" | ||||
|      showgrid="false" | ||||
|      inkscape:window-width="2560" | ||||
|      inkscape:window-height="1364" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
|      borderlayer="true" | ||||
|      inkscape:showpageshadow="false" | ||||
|      inkscape:snap-nodes="false" | ||||
|      inkscape:snap-bbox="true" | ||||
|      showborder="true"> | ||||
|     <inkscape:grid | ||||
|        type="xygrid" | ||||
|        id="grid12954" | ||||
|        empspacing="5" | ||||
|        visible="true" | ||||
|        enabled="true" | ||||
|        snapvisiblegridlinesonly="true" /> | ||||
|   </sodipodi:namedview> | ||||
|   <metadata | ||||
|      id="metadata2862"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <dc:title></dc:title> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <g | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1" | ||||
|      transform="translate(-444.64286,-781.36218)"> | ||||
|     <g | ||||
|        transform="matrix(0.6526046,0,0,0.80554422,99.592644,-636.32172)" | ||||
|        id="g37994"> | ||||
|       <g | ||||
|          transform="matrix(1.5323214,0,0,1.2413968,-324.76058,489.69039)" | ||||
|          id="toggle-off" | ||||
|          inkscape:label="#g8477"> | ||||
|         <circle | ||||
|            cy="1033.993" | ||||
|            cx="571.95966" | ||||
|            id="path8444" | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#555753;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|            r="7" /> | ||||
|         <rect | ||||
|            ry="2.0108337" | ||||
|            rx="1.9562569" | ||||
|            y="1031.9885" | ||||
|            x="565.0083" | ||||
|            height="4.0216675" | ||||
|            width="34.850178" | ||||
|            id="rect8461" | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#555753;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" /> | ||||
|       </g> | ||||
|       <g | ||||
|          transform="matrix(1.5323214,0,0,1.2413968,-324.85635,491.16456)" | ||||
|          id="toggle-on" | ||||
|          inkscape:label="#g8481"> | ||||
|         <rect | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|            id="rect8475" | ||||
|            width="34.850178" | ||||
|            height="4.0216675" | ||||
|            x="565.0083" | ||||
|            y="1070.9279" | ||||
|            rx="1.9562569" | ||||
|            ry="2.0108337" /> | ||||
|         <circle | ||||
|            transform="scale(-1,1)" | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|            id="circle8463" | ||||
|            cx="-591.0213" | ||||
|            cy="1072.9402" | ||||
|            r="9" /> | ||||
|       </g> | ||||
|     </g> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 6.1 KiB | 
							
								
								
									
										113
									
								
								data/theme/toggle-on-hc.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						| @@ -0,0 +1,113 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    width="48" | ||||
|    height="22" | ||||
|    id="svg2857" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="toggle-on-hc.svg"> | ||||
|   <defs | ||||
|      id="defs2859"> | ||||
|     <inkscape:perspective | ||||
|        sodipodi:type="inkscape:persp3d" | ||||
|        inkscape:vp_x="0 : 526.18109 : 1" | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_z="744.09448 : 526.18109 : 1" | ||||
|        inkscape:persp3d-origin="372.04724 : 350.78739 : 1" | ||||
|        id="perspective2865" /> | ||||
|     <inkscape:perspective | ||||
|        id="perspective2843" | ||||
|        inkscape:persp3d-origin="0.5 : 0.33333333 : 1" | ||||
|        inkscape:vp_z="1 : 0.5 : 1" | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_x="0 : 0.5 : 1" | ||||
|        sodipodi:type="inkscape:persp3d" /> | ||||
|     <inkscape:path-effect | ||||
|        effect="spiro" | ||||
|        id="path-effect77541-4" | ||||
|        is_visible="true" /> | ||||
|   </defs> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
|      pagecolor="#000000" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="1" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="1" | ||||
|      inkscape:cx="-222.95215" | ||||
|      inkscape:cy="3.9378433" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="g37994" | ||||
|      showgrid="false" | ||||
|      inkscape:window-width="2560" | ||||
|      inkscape:window-height="1364" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
|      borderlayer="true" | ||||
|      inkscape:showpageshadow="false" | ||||
|      inkscape:snap-nodes="false" | ||||
|      inkscape:snap-bbox="true" | ||||
|      showborder="true"> | ||||
|     <inkscape:grid | ||||
|        type="xygrid" | ||||
|        id="grid12954" | ||||
|        empspacing="5" | ||||
|        visible="true" | ||||
|        enabled="true" | ||||
|        snapvisiblegridlinesonly="true" /> | ||||
|   </sodipodi:namedview> | ||||
|   <metadata | ||||
|      id="metadata2862"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <dc:title></dc:title> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <g | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      id="layer1" | ||||
|      transform="translate(-444.64286,-781.36218)"> | ||||
|     <g | ||||
|        transform="matrix(0.6526046,0,0,0.80554422,99.592644,-636.32172)" | ||||
|        id="g37994"> | ||||
|       <g | ||||
|          transform="matrix(1.5323214,0,0,1.2413968,-324.85635,441.50868)" | ||||
|          id="toggle-on" | ||||
|          inkscape:label="#g8481"> | ||||
|         <rect | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|            id="rect8475" | ||||
|            width="34.850178" | ||||
|            height="4.0216675" | ||||
|            x="565.0083" | ||||
|            y="1070.9279" | ||||
|            rx="1.9562569" | ||||
|            ry="2.0108337" /> | ||||
|         <circle | ||||
|            transform="scale(-1,1)" | ||||
|            style="color:#000000;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#3465a4;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;marker:none;marker-start:none;marker-mid:none;marker-end:none;paint-order:normal;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate" | ||||
|            id="circle8463" | ||||
|            cx="-591.0213" | ||||
|            cy="1072.9402" | ||||
|            r="9" /> | ||||
|       </g> | ||||
|     </g> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 4.4 KiB | 
| @@ -14,7 +14,7 @@ | ||||
|    height="22" | ||||
|    id="svg2857" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.48.5 r10040" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="toggle-on-intl.svg"> | ||||
|   <defs | ||||
|      id="defs2859"> | ||||
| @@ -52,7 +52,7 @@ | ||||
|       <stop | ||||
|          id="stop77465" | ||||
|          offset="1" | ||||
|          style="stop-color:#204a87;stop-opacity:1" /> | ||||
|          style="stop-color:#205b9a;stop-opacity:1" /> | ||||
|     </linearGradient> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
| @@ -88,14 +88,14 @@ | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="1" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="32" | ||||
|      inkscape:cx="17.255148" | ||||
|      inkscape:cy="8.9252639" | ||||
|      inkscape:zoom="1" | ||||
|      inkscape:cx="37.410841" | ||||
|      inkscape:cy="16.009314" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="g37994" | ||||
|      showgrid="true" | ||||
|      showgrid="false" | ||||
|      inkscape:window-width="2560" | ||||
|      inkscape:window-height="1375" | ||||
|      inkscape:window-height="1376" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
|   | ||||
| Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 6.8 KiB | 
| @@ -14,7 +14,7 @@ | ||||
|    height="22" | ||||
|    id="svg2857" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.48.5 r10040" | ||||
|    inkscape:version="0.91 r13725" | ||||
|    sodipodi:docname="toggle-on-us.svg"> | ||||
|   <defs | ||||
|      id="defs2859"> | ||||
| @@ -32,28 +32,6 @@ | ||||
|        inkscape:vp_y="0 : 1000 : 0" | ||||
|        inkscape:vp_x="0 : 0.5 : 1" | ||||
|        sodipodi:type="inkscape:persp3d" /> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient77461" | ||||
|        id="linearGradient77551" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(1.3066667,0,0,1,-841.64667,-483)" | ||||
|        x1="1164.7644" | ||||
|        y1="962.93695" | ||||
|        x2="1164.7644" | ||||
|        y2="970.51404" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient77461" | ||||
|        inkscape:collect="always"> | ||||
|       <stop | ||||
|          id="stop77463" | ||||
|          offset="0" | ||||
|          style="stop-color:#182f4c;stop-opacity:1" /> | ||||
|       <stop | ||||
|          id="stop77465" | ||||
|          offset="1" | ||||
|          style="stop-color:#204a87;stop-opacity:1" /> | ||||
|     </linearGradient> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient76469-7-7-4" | ||||
| @@ -80,6 +58,38 @@ | ||||
|        effect="spiro" | ||||
|        id="path-effect77541-4" | ||||
|        is_visible="true" /> | ||||
|     <linearGradient | ||||
|        id="linearGradient77461-1" | ||||
|        inkscape:collect="always"> | ||||
|       <stop | ||||
|          id="stop77463-1" | ||||
|          offset="0" | ||||
|          style="stop-color:#182f4c;stop-opacity:1" /> | ||||
|       <stop | ||||
|          id="stop77465-4" | ||||
|          offset="1" | ||||
|          style="stop-color:#205b9a;stop-opacity:1" /> | ||||
|     </linearGradient> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient77461-1" | ||||
|        id="linearGradient77551-6-5" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(0.8527367,0,0,0.80554422,-969.41608,-778.00299)" | ||||
|        x1="1164.7644" | ||||
|        y1="962.93695" | ||||
|        x2="1164.7644" | ||||
|        y2="970.51404" /> | ||||
|     <linearGradient | ||||
|        inkscape:collect="always" | ||||
|        xlink:href="#linearGradient77461-1" | ||||
|        id="linearGradient11198" | ||||
|        gradientUnits="userSpaceOnUse" | ||||
|        gradientTransform="matrix(1.3066667,0,0,1,-1066.3709,794.25325)" | ||||
|        x1="1322.5831" | ||||
|        y1="-312.51855" | ||||
|        x2="1322.5831" | ||||
|        y2="-306.53461" /> | ||||
|   </defs> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
| @@ -89,13 +99,13 @@ | ||||
|      inkscape:pageopacity="1" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="1" | ||||
|      inkscape:cx="33.380898" | ||||
|      inkscape:cy="6.9658271" | ||||
|      inkscape:cx="-26.798898" | ||||
|      inkscape:cy="5.3753009" | ||||
|      inkscape:document-units="px" | ||||
|      inkscape:current-layer="g37994" | ||||
|      showgrid="false" | ||||
|      inkscape:window-width="2560" | ||||
|      inkscape:window-height="1375" | ||||
|      inkscape:window-height="1376" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
| @@ -152,7 +162,7 @@ | ||||
|            height="25" | ||||
|            width="98" | ||||
|            id="rect38000" | ||||
|            style="color:#000000;fill:url(#linearGradient77551);fill-opacity:1;fill-rule:nonzero;stroke:#182f4c;stroke-width:1.37920964;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate" /> | ||||
|            style="color:#000000;fill:url(#linearGradient11198);fill-opacity:1;fill-rule:nonzero;stroke:#182f4c;stroke-width:1.37920964;stroke-linecap:round;stroke-linejoin:miter;stroke-miterlimit:4;stroke-opacity:1;stroke-dasharray:none;stroke-dashoffset:0;marker:none;visibility:visible;display:inline;overflow:visible;enable-background:accumulate;clip-rule:nonzero;opacity:1;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;filter-blend-mode:normal;filter-gaussianBlur-deviation:0;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto" /> | ||||
|       </g> | ||||
|       <g | ||||
|          transform="translate(2.0625,-2)" | ||||
|   | ||||
| Before Width: | Height: | Size: 7.0 KiB After Width: | Height: | Size: 7.7 KiB | 
| @@ -35,7 +35,6 @@ | ||||
|       <xi:include href="xml/st-bin.xml"/> | ||||
|       <xi:include href="xml/st-box-layout.xml"/> | ||||
|       <xi:include href="xml/st-scroll-view.xml"/> | ||||
|       <xi:include href="xml/st-table.xml"/> | ||||
|     </chapter> | ||||
|  | ||||
|     <chapter id="styling"> | ||||
|   | ||||
| @@ -54,7 +54,6 @@ const Application = new Lang.Class({ | ||||
|         this._startupUuid = null; | ||||
|         this._loaded = false; | ||||
|         this._skipMainWindow = false; | ||||
|         this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); | ||||
|     }, | ||||
|  | ||||
|     _extensionAvailable: function(uuid) { | ||||
|   | ||||
| @@ -14,7 +14,7 @@ const ShellEntry = imports.ui.shellEntry; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const UserWidget = imports.ui.userWidget; | ||||
|  | ||||
| const DEFAULT_BUTTON_WELL_ICON_SIZE = 24; | ||||
| const DEFAULT_BUTTON_WELL_ICON_SIZE = 16; | ||||
| const DEFAULT_BUTTON_WELL_ANIMATION_DELAY = 1.0; | ||||
| const DEFAULT_BUTTON_WELL_ANIMATION_TIME = 0.3; | ||||
|  | ||||
| @@ -189,22 +189,21 @@ const AuthPrompt = new Lang.Class({ | ||||
|                                              this._updateNextButtonSensitivity(this._entry.text.length > 0); | ||||
|                                          })); | ||||
|         this._entry.clutter_text.connect('activate', Lang.bind(this, function() { | ||||
|             this.emit('next'); | ||||
|             if (this.nextButton.reactive) | ||||
|                 this.emit('next'); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     _onAskQuestion: function(verifier, serviceName, question, passwordChar) { | ||||
|         if (this._preemptiveAnswer) { | ||||
|             if (this._queryingService) | ||||
|                 this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer); | ||||
|             this._preemptiveAnswer = null; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (this._queryingService) | ||||
|             this.clear(); | ||||
|  | ||||
|         this._queryingService = serviceName; | ||||
|         if (this._preemptiveAnswer) { | ||||
|             this._userVerifier.answerQuery(this._queryingService, this._preemptiveAnswer); | ||||
|             this._preemptiveAnswer = null; | ||||
|             return; | ||||
|         } | ||||
|         this.setPasswordChar(passwordChar); | ||||
|         this.setQuestion(question); | ||||
|  | ||||
| @@ -260,6 +259,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onVerificationComplete: function() { | ||||
|         this.setActorInDefaultButtonWell(null); | ||||
|         this.verificationStatus = AuthPromptStatus.VERIFICATION_SUCCEEDED; | ||||
| 	this.cancelButton.reactive = false; | ||||
|     }, | ||||
| @@ -283,6 +283,12 @@ const AuthPrompt = new Lang.Class({ | ||||
|         if (oldActor) | ||||
|             Tweener.removeTweens(oldActor); | ||||
|  | ||||
|         let wasSpinner; | ||||
|         if (oldActor == this._spinner.actor) | ||||
|             wasSpinner = true; | ||||
|         else | ||||
|             wasSpinner = false; | ||||
|  | ||||
|         let isSpinner; | ||||
|         if (actor == this._spinner.actor) | ||||
|             isSpinner = true; | ||||
| @@ -292,6 +298,11 @@ const AuthPrompt = new Lang.Class({ | ||||
|         if (this._defaultButtonWellActor != actor && oldActor) { | ||||
|             if (!animate) { | ||||
|                 oldActor.opacity = 0; | ||||
|  | ||||
|                 if (wasSpinner) { | ||||
|                     if (this._spinner) | ||||
|                         this._spinner.stop(); | ||||
|                 } | ||||
|             } else { | ||||
|                 Tweener.addTween(oldActor, | ||||
|                                  { opacity: 0, | ||||
| @@ -300,7 +311,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|                                    transition: 'linear', | ||||
|                                    onCompleteScope: this, | ||||
|                                    onComplete: function() { | ||||
|                                       if (isSpinner) { | ||||
|                                       if (wasSpinner) { | ||||
|                                           if (this._spinner) | ||||
|                                               this._spinner.stop(); | ||||
|                                       } | ||||
| @@ -403,7 +414,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     updateSensitivity: function(sensitive) { | ||||
|         this._updateNextButtonSensitivity(sensitive); | ||||
|         this._updateNextButtonSensitivity(sensitive && this._entry.text.length > 0); | ||||
|         this._entry.reactive = sensitive; | ||||
|         this._entry.clutter_text.editable = sensitive; | ||||
|     }, | ||||
| @@ -434,8 +445,9 @@ const AuthPrompt = new Lang.Class({ | ||||
|         let oldStatus = this.verificationStatus; | ||||
|         this.verificationStatus = AuthPromptStatus.NOT_VERIFYING; | ||||
|         this.cancelButton.reactive = true; | ||||
|         this.nextButton.label = _("Next"); | ||||
|  | ||||
|         if (oldStatus == AuthPromptStatus.VERIFYING) | ||||
|         if (this._userVerifier) | ||||
|             this._userVerifier.cancel(); | ||||
|  | ||||
|         this._queryingService = null; | ||||
| @@ -490,6 +502,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|  | ||||
|     finish: function(onComplete) { | ||||
|         if (!this._userVerifier.hasPendingMessages) { | ||||
|             this._userVerifier.clear(); | ||||
|             onComplete(); | ||||
|             return; | ||||
|         } | ||||
| @@ -497,12 +510,13 @@ const AuthPrompt = new Lang.Class({ | ||||
|         let signalId = this._userVerifier.connect('no-more-messages', | ||||
|                                                   Lang.bind(this, function() { | ||||
|                                                       this._userVerifier.disconnect(signalId); | ||||
|                                                       this._userVerifier.clear(); | ||||
|                                                       onComplete(); | ||||
|                                                   })); | ||||
|     }, | ||||
|  | ||||
|     cancel: function() { | ||||
|         if (this.verificationStatus == AuthPromptStatus.NOT_VERIFYING || this.verificationStatus == AuthPromptStatus.VERIFICATION_SUCCEEDED) { | ||||
|         if (this.verificationStatus == AuthPromptStatus.VERIFICATION_SUCCEEDED) { | ||||
|             return; | ||||
|         } | ||||
|         this.reset(); | ||||
|   | ||||
| @@ -16,6 +16,34 @@ | ||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| /* | ||||
|  * In order for transformation animations to look good, they need to be | ||||
|  * incremental and have some order to them (e.g., fade out hidden items, | ||||
|  * then shrink to close the void left over). Chaining animations in this way can | ||||
|  * be error-prone and wordy using just Tweener callbacks. | ||||
|  * | ||||
|  * The classes in this file help with this: | ||||
|  * | ||||
|  * - Task.  encapsulates schedulable work to be run in a specific scope. | ||||
|  * | ||||
|  * - ConsecutiveBatch.  runs a series of tasks in order and completes | ||||
|  *                      when the last in the series finishes. | ||||
|  * | ||||
|  * - ConcurrentBatch.  runs a set of tasks at the same time and completes | ||||
|  *                     when the last to finish completes. | ||||
|  * | ||||
|  * - Hold.  prevents a batch from completing the pending task until | ||||
|  *          the hold is released. | ||||
|  * | ||||
|  * The tasks associated with a batch are specified in a list at batch | ||||
|  * construction time as either task objects or plain functions. | ||||
|  * Batches are task objects, themselves, so they can be nested. | ||||
|  * | ||||
|  * These classes aren't specific to GDM, but were found to be unintuitive and so | ||||
|  * are not used elsewhere. These APIs may ultimately get dropped entirely and | ||||
|  * replaced by something else. | ||||
|  */ | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
|   | ||||
| @@ -22,6 +22,7 @@ const Clutter = imports.gi.Clutter; | ||||
| const Gdm = imports.gi.Gdm; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const GObject = imports.gi.GObject; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| @@ -48,6 +49,7 @@ const _FADE_ANIMATION_TIME = 0.25; | ||||
| const _SCROLL_ANIMATION_TIME = 0.5; | ||||
| const _TIMED_LOGIN_IDLE_THRESHOLD = 5.0; | ||||
| const _LOGO_ICON_HEIGHT = 48; | ||||
| const _MAX_BOTTOM_MENU_ITEMS = 5; | ||||
|  | ||||
| const UserListItem = new Lang.Class({ | ||||
|     Name: 'UserListItem', | ||||
| @@ -71,6 +73,9 @@ const UserListItem = new Lang.Class({ | ||||
|         this._userWidget = new UserWidget.UserWidget(this.user); | ||||
|         layout.add(this._userWidget.actor); | ||||
|  | ||||
|         this._userWidget.actor.bind_property('label-actor', this.actor, 'label-actor', | ||||
|                                              GObject.BindingFlags.SYNC_CREATE); | ||||
|  | ||||
|         this._timedLoginIndicator = new St.Bin({ style_class: 'login-dialog-timed-login-indicator', | ||||
|                                                  scale_x: 0 }); | ||||
|         layout.add(this._timedLoginIndicator); | ||||
| @@ -91,7 +96,7 @@ const UserListItem = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
|         this._user.disconnect(this._userChangedId); | ||||
|         this.user.disconnect(this._userChangedId); | ||||
|     }, | ||||
|  | ||||
|     _onClicked: function() { | ||||
| @@ -207,6 +212,10 @@ const UserList = new Lang.Class({ | ||||
|         return item; | ||||
|     }, | ||||
|  | ||||
|     containsUser: function(user) { | ||||
|         return this._items[user.get_user_name()] != null; | ||||
|     }, | ||||
|  | ||||
|     addUser: function(user) { | ||||
|         if (!user.is_loaded) | ||||
|             return; | ||||
| @@ -279,7 +288,16 @@ const SessionMenuButton = new Lang.Class({ | ||||
|  | ||||
|         this.actor = new St.Bin({ child: this._button }); | ||||
|  | ||||
|         this._menu = new PopupMenu.PopupMenu(this._button, 0, St.Side.TOP); | ||||
|         let side = St.Side.TOP; | ||||
|         let align = 0; | ||||
|         if (Gdm.get_session_ids().length > _MAX_BOTTOM_MENU_ITEMS) { | ||||
|             if (this.actor.text_direction == Clutter.TextDirection.RTL) | ||||
|                 side = St.Side.RIGHT; | ||||
|             else | ||||
|                 side = St.Side.LEFT; | ||||
|             align = 0.5; | ||||
|         } | ||||
|         this._menu = new PopupMenu.PopupMenu(this._button, align, side); | ||||
|         Main.uiGroup.add_actor(this._menu.actor); | ||||
|         this._menu.actor.hide(); | ||||
|  | ||||
| @@ -492,7 +510,7 @@ const LoginDialog = new Lang.Class({ | ||||
|         let [minWidth, minHeight, natWidth, natHeight] = this._bannerView.get_preferred_size(); | ||||
|         let centerX = dialogBox.x1 + (dialogBox.x2 - dialogBox.x1) / 2; | ||||
|  | ||||
|         actorBox.x1 = centerX - natWidth / 2; | ||||
|         actorBox.x1 = Math.floor(centerX - natWidth / 2); | ||||
|         actorBox.y1 = dialogBox.y1 + Main.layoutManager.panelBox.height; | ||||
|         actorBox.x2 = actorBox.x1 + natWidth; | ||||
|         actorBox.y2 = actorBox.y1 + natHeight; | ||||
| @@ -506,7 +524,7 @@ const LoginDialog = new Lang.Class({ | ||||
|         let [minWidth, minHeight, natWidth, natHeight] = this._logoBin.get_preferred_size(); | ||||
|         let centerX = dialogBox.x1 + (dialogBox.x2 - dialogBox.x1) / 2; | ||||
|  | ||||
|         actorBox.x1 = centerX - natWidth / 2; | ||||
|         actorBox.x1 = Math.floor(centerX - natWidth / 2); | ||||
|         actorBox.y1 = dialogBox.y2 - natHeight; | ||||
|         actorBox.x2 = actorBox.x1 + natWidth; | ||||
|         actorBox.y2 = actorBox.y1 + natHeight; | ||||
| @@ -521,8 +539,11 @@ const LoginDialog = new Lang.Class({ | ||||
|         let centerX = dialogBox.x1 + (dialogBox.x2 - dialogBox.x1) / 2; | ||||
|         let centerY = dialogBox.y1 + (dialogBox.y2 - dialogBox.y1) / 2; | ||||
|  | ||||
|         actorBox.x1 = centerX - natWidth / 2; | ||||
|         actorBox.y1 = centerY - natHeight / 2; | ||||
|         natWidth = Math.min(natWidth, dialogBox.x2 - dialogBox.x1); | ||||
|         natHeight = Math.min(natHeight, dialogBox.y2 - dialogBox.y1); | ||||
|  | ||||
|         actorBox.x1 = Math.floor(centerX - natWidth / 2); | ||||
|         actorBox.y1 = Math.floor(centerY - natHeight / 2); | ||||
|         actorBox.x2 = actorBox.x1 + natWidth; | ||||
|         actorBox.y2 = actorBox.y1 + natHeight; | ||||
|  | ||||
| @@ -570,14 +591,21 @@ const LoginDialog = new Lang.Class({ | ||||
|         // try a different layout, or if we have what extra space we | ||||
|         // can hand out | ||||
|         if (bannerAllocation) { | ||||
|             let leftOverYSpace = dialogHeight - bannerHeight - authPromptHeight - logoHeight; | ||||
|             let bannerSpace; | ||||
|  | ||||
|             if (authPromptAllocation) | ||||
|                 bannerSpace = authPromptAllocation.y1 - bannerAllocation.y1; | ||||
|             else | ||||
|                 bannerSpace = 0; | ||||
|  | ||||
|             let leftOverYSpace = bannerSpace - bannerHeight; | ||||
|  | ||||
|             if (leftOverYSpace > 0) { | ||||
|                  // First figure out how much left over space is up top | ||||
|                  let leftOverTopSpace = leftOverYSpace / 2; | ||||
|  | ||||
|                  // Then, shift the banner into the middle of that extra space | ||||
|                  let yShift = leftOverTopSpace / 2; | ||||
|                  let yShift = Math.floor(leftOverTopSpace / 2); | ||||
|  | ||||
|                  bannerAllocation.y1 += yShift; | ||||
|                  bannerAllocation.y2 += yShift; | ||||
| @@ -603,8 +631,8 @@ const LoginDialog = new Lang.Class({ | ||||
|                      let centerGap = wideSpacing / 8; | ||||
|  | ||||
|                      // place the banner along the left edge of the center margin | ||||
|                      bannerAllocation.x2 = centerX - centerGap / 2; | ||||
|                      bannerAllocation.x1 = bannerAllocation.x2 - wideBannerWidth; | ||||
|                      bannerAllocation.x2 = Math.floor(centerX - centerGap / 2); | ||||
|                      bannerAllocation.x1 = Math.floor(bannerAllocation.x2 - wideBannerWidth); | ||||
|  | ||||
|                      // figure out how tall it would like to be and try to accomodate | ||||
|                      // but don't let it get too close to the logo | ||||
| @@ -612,11 +640,11 @@ const LoginDialog = new Lang.Class({ | ||||
|  | ||||
|                      let maxWideHeight = dialogHeight - 3 * logoHeight; | ||||
|                      wideBannerHeight = Math.min(maxWideHeight, wideBannerHeight); | ||||
|                      bannerAllocation.y1 = centerY - wideBannerHeight / 2; | ||||
|                      bannerAllocation.y1 = Math.floor(centerY - wideBannerHeight / 2); | ||||
|                      bannerAllocation.y2 = bannerAllocation.y1 + wideBannerHeight; | ||||
|  | ||||
|                      // place the auth prompt along the right edge of the center margin | ||||
|                      authPromptAllocation.x1 = centerX + centerGap / 2; | ||||
|                      authPromptAllocation.x1 = Math.floor(centerX + centerGap / 2); | ||||
|                      authPromptAllocation.x2 = authPromptAllocation.x1 + authPromptWidth; | ||||
|                  } else { | ||||
|                      // If we aren't going to do a wide view, then we need to limit | ||||
| @@ -626,7 +654,7 @@ const LoginDialog = new Lang.Class({ | ||||
|                      leftOverYSpace += bannerHeight; | ||||
|  | ||||
|                      // Then figure out how much of that space is up top | ||||
|                      let availableTopSpace = leftOverYSpace / 2; | ||||
|                      let availableTopSpace = Math.floor(leftOverYSpace / 2); | ||||
|  | ||||
|                      // Then give all of that space to the banner | ||||
|                      bannerAllocation.y2 = bannerAllocation.y1 + availableTopSpace; | ||||
| @@ -637,7 +665,7 @@ const LoginDialog = new Lang.Class({ | ||||
|             let leftOverYSpace = dialogHeight - userSelectionHeight - logoHeight; | ||||
|  | ||||
|             if (leftOverYSpace > 0) { | ||||
|                 let topExpansion = leftOverYSpace / 2; | ||||
|                 let topExpansion = Math.floor(leftOverYSpace / 2); | ||||
|                 let bottomExpansion = topExpansion; | ||||
|  | ||||
|                 userSelectionAllocation.y1 -= topExpansion; | ||||
| @@ -853,7 +881,7 @@ const LoginDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _loginScreenSessionActivated: function() { | ||||
|         if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFICATION_SUCCEEDED) | ||||
|         if (this.actor.opacity == 255 && this._authPrompt.verificationStatus == AuthPrompt.AuthPromptStatus.NOT_VERIFYING) | ||||
|             return; | ||||
|  | ||||
|         Tweener.addTween(this.actor, | ||||
| @@ -870,7 +898,8 @@ const LoginDialog = new Lang.Class({ | ||||
|                            }, | ||||
|                            onUpdateScope: this, | ||||
|                            onComplete: function() { | ||||
|                                this._authPrompt.reset(); | ||||
|                                if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.NOT_VERIFYING) | ||||
|                                    this._authPrompt.reset(); | ||||
|                            }, | ||||
|                            onCompleteScope: this }); | ||||
|     }, | ||||
| @@ -899,11 +928,7 @@ const LoginDialog = new Lang.Class({ | ||||
|                            }, | ||||
|                            onUpdateScope: this, | ||||
|                            onComplete: function() { | ||||
|                                let id = Mainloop.idle_add(Lang.bind(this, function() { | ||||
|                                    this._greeter.call_start_session_when_ready_sync(serviceName, true, null); | ||||
|                                    return GLib.SOURCE_REMOVE; | ||||
|                                })); | ||||
|                                GLib.Source.set_name_by_id(id, '[gnome-shell] this._greeter.call_start_session_when_ready_sync'); | ||||
|                                this._greeter.call_start_session_when_ready_sync(serviceName, true, null); | ||||
|                            }, | ||||
|                            onCompleteScope: this }); | ||||
|     }, | ||||
| @@ -1083,18 +1108,11 @@ const LoginDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onUserListActivated: function(activatedItem) { | ||||
|         let tasks = [function() { | ||||
|                          return GdmUtil.cloneAndFadeOutActor(this._userSelectionBox); | ||||
|                      }, | ||||
|                      function() { | ||||
|                          this._setUserListExpanded(false); | ||||
|                      }]; | ||||
|  | ||||
|         this._user = activatedItem.user; | ||||
|  | ||||
|         this._updateCancelButton(); | ||||
|  | ||||
|         let batch = new Batch.ConcurrentBatch(this, [new Batch.ConsecutiveBatch(this, tasks), | ||||
|         let batch = new Batch.ConcurrentBatch(this, [GdmUtil.cloneAndFadeOutActor(this._userSelectionBox), | ||||
|                                                      this._beginVerificationForItem(activatedItem)]); | ||||
|         batch.run(); | ||||
|     }, | ||||
| @@ -1112,6 +1130,10 @@ const LoginDialog = new Lang.Class({ | ||||
|             this._userManager.disconnect(this._userRemovedId); | ||||
|             this._userRemovedId = 0; | ||||
|         } | ||||
|         if (this._userChangedId) { | ||||
|             this._userManager.disconnect(this._userChangedId); | ||||
|             this._userChangedId = 0; | ||||
|         } | ||||
|         this._textureCache.disconnect(this._updateLogoTextureId); | ||||
|         Main.layoutManager.disconnect(this._startupCompleteId); | ||||
|         if (this._settings) { | ||||
| @@ -1158,6 +1180,14 @@ const LoginDialog = new Lang.Class({ | ||||
|                                                             this._userList.removeUser(user); | ||||
|                                                         })); | ||||
|  | ||||
|         this._userChangedId = this._userManager.connect('user-changed', | ||||
|                                                         Lang.bind(this, function(userManager, user) { | ||||
|                                                             if (this._userList.containsUser(user) && user.locked) | ||||
|                                                                 this._userList.removeUser(user); | ||||
|                                                             else if (!this._userList.containsUser(user) && !user.locked) | ||||
|                                                                 this._userList.addUser(user); | ||||
|                                                         })); | ||||
|  | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|   | ||||
| @@ -35,8 +35,8 @@ const ALLOWED_FAILURES_KEY = 'allowed-failures'; | ||||
| const LOGO_KEY = 'logo'; | ||||
| const DISABLE_USER_LIST_KEY = 'disable-user-list'; | ||||
|  | ||||
| // Give user 16ms to read each character of a PAM message | ||||
| const USER_READ_TIME = 16 | ||||
| // Give user 48ms to read each character of a PAM message | ||||
| const USER_READ_TIME = 48 | ||||
|  | ||||
| const MessageType = { | ||||
|     NONE: 0, | ||||
| @@ -410,7 +410,7 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|     _updateDefaultService: function() { | ||||
|         if (this._settings.get_boolean(PASSWORD_AUTHENTICATION_KEY)) | ||||
|             this._defaultService = PASSWORD_SERVICE_NAME; | ||||
|         else if (this.smartcardDetected) | ||||
|         else if (this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY)) | ||||
|             this._defaultService = SMARTCARD_SERVICE_NAME; | ||||
|         else if (this._haveFingerprintReader) | ||||
|             this._defaultService = FINGERPRINT_SERVICE_NAME; | ||||
|   | ||||
| @@ -134,7 +134,7 @@ const LoginManagerSystemd = new Lang.Class({ | ||||
|             if (error) | ||||
|                 asyncCallback(false); | ||||
|             else | ||||
|                 asyncCallback(result[0] != 'no'); | ||||
|                 asyncCallback(result[0] != 'no' && result[0] != 'na'); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|   | ||||
| @@ -448,8 +448,6 @@ const AppSwitcher = new Lang.Class({ | ||||
|             }); | ||||
|             if (appIcon.cachedWindows.length > 0) | ||||
|                 this._addIcon(appIcon); | ||||
|             else if (workspace == null) | ||||
|                 throw new Error('%s appears to be running, but doesn\'t have any windows'.format(appIcon.app.get_name())); | ||||
|         } | ||||
|  | ||||
|         this._curApp = -1; | ||||
| @@ -687,15 +685,17 @@ const WindowIcon = new Lang.Class({ | ||||
|  | ||||
|         this._icon.destroy_all_children(); | ||||
|  | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|  | ||||
|         switch (mode) { | ||||
|             case AppIconMode.THUMBNAIL_ONLY: | ||||
|                 size = WINDOW_PREVIEW_SIZE; | ||||
|                 this._icon.add_actor(_createWindowClone(mutterWindow, WINDOW_PREVIEW_SIZE)); | ||||
|                 this._icon.add_actor(_createWindowClone(mutterWindow, size * scaleFactor)); | ||||
|                 break; | ||||
|  | ||||
|             case AppIconMode.BOTH: | ||||
|                 size = WINDOW_PREVIEW_SIZE; | ||||
|                 this._icon.add_actor(_createWindowClone(mutterWindow, WINDOW_PREVIEW_SIZE)); | ||||
|                 this._icon.add_actor(_createWindowClone(mutterWindow, size * scaleFactor)); | ||||
|  | ||||
|                 if (this.app) | ||||
|                     this._icon.add_actor(this._createAppIcon(this.app, | ||||
| @@ -707,7 +707,7 @@ const WindowIcon = new Lang.Class({ | ||||
|                 this._icon.add_actor(this._createAppIcon(this.app, size)); | ||||
|         } | ||||
|  | ||||
|         this._icon.set_size(size, size); | ||||
|         this._icon.set_size(size * scaleFactor, size * scaleFactor); | ||||
|     }, | ||||
|  | ||||
|     _createAppIcon: function(app, size) { | ||||
|   | ||||
| @@ -7,7 +7,7 @@ const St = imports.gi.St; | ||||
| const Signals = imports.signals; | ||||
| const Atk = imports.gi.Atk; | ||||
|  | ||||
| const ANIMATED_ICON_UPDATE_TIMEOUT = 100; | ||||
| const ANIMATED_ICON_UPDATE_TIMEOUT = 16; | ||||
|  | ||||
| const Animation = new Lang.Class({ | ||||
|     Name: 'Animation', | ||||
| @@ -33,7 +33,7 @@ const Animation = new Lang.Class({ | ||||
|             if (this._frame == 0) | ||||
|                 this._showFrame(0); | ||||
|  | ||||
|             this._timeoutId = Mainloop.timeout_add(this._speed, Lang.bind(this, this._update)); | ||||
|             this._timeoutId = GLib.timeout_add(GLib.PRIORITY_LOW, this._speed, Lang.bind(this, this._update)); | ||||
|             GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._update'); | ||||
|         } | ||||
|  | ||||
|   | ||||
| @@ -366,8 +366,6 @@ const AllView = new Lang.Class({ | ||||
|     Extends: BaseAppView, | ||||
|  | ||||
|     _init: function() { | ||||
|         this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); | ||||
|  | ||||
|         this.parent({ usePagination: true }, null); | ||||
|         this._scrollView = new St.ScrollView({ style_class: 'all-apps', | ||||
|                                                x_expand: true, | ||||
| @@ -502,6 +500,11 @@ const AllView = new Lang.Class({ | ||||
|  | ||||
|     _loadApps: function() { | ||||
|         let apps = Gio.AppInfo.get_all().filter(function(appInfo) { | ||||
|             try { | ||||
|                 let id = appInfo.get_id(); // catch invalid file encodings | ||||
|             } catch(e) { | ||||
|                 return false; | ||||
|             } | ||||
|             return appInfo.should_show(); | ||||
|         }).map(function(app) { | ||||
|             return app.get_id(); | ||||
| @@ -525,7 +528,7 @@ const AllView = new Lang.Class({ | ||||
|         // at least on single-monitor setups. | ||||
|         // This also disables drag-to-launch on multi-monitor setups, | ||||
|         // but we hope that is not used much. | ||||
|         let favoritesWritable = this._settings.is_writable('favorite-apps'); | ||||
|         let favoritesWritable = global.settings.is_writable('favorite-apps'); | ||||
|  | ||||
|         apps.forEach(Lang.bind(this, function(appId) { | ||||
|             let app = appSys.lookup_app(appId); | ||||
| @@ -755,7 +758,8 @@ const AllView = new Lang.Class({ | ||||
|         let fadeOffset = Math.min(this._grid.topPadding, | ||||
|                                   this._grid.bottomPadding); | ||||
|         this._scrollView.update_fade_effect(fadeOffset, 0); | ||||
|         this._scrollView.get_effect('fade').fade_edges = true; | ||||
|         if (fadeOffset > 0) | ||||
|             this._scrollView.get_effect('fade').fade_edges = true; | ||||
|  | ||||
|         if (this._availWidth != availWidth || this._availHeight != availHeight || oldNPages != this._grid.nPages()) { | ||||
|             this._adjustment.value = 0; | ||||
| @@ -783,8 +787,6 @@ const FrequentView = new Lang.Class({ | ||||
|     _init: function() { | ||||
|         this.parent(null, { fillParent: true }); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); | ||||
|  | ||||
|         this.actor = new St.Widget({ style_class: 'frequent-apps', | ||||
|                                      layout_manager: new Clutter.BinLayout(), | ||||
|                                      x_expand: true, y_expand: true }); | ||||
| @@ -827,7 +829,7 @@ const FrequentView = new Lang.Class({ | ||||
|         // at least on single-monitor setups. | ||||
|         // This also disables drag-to-launch on multi-monitor setups, | ||||
|         // but we hope that is not used much. | ||||
|         let favoritesWritable = this._settings.is_writable('favorite-apps'); | ||||
|         let favoritesWritable = global.settings.is_writable('favorite-apps'); | ||||
|  | ||||
|         for (let i = 0; i < mostUsed.length; i++) { | ||||
|             if (!mostUsed[i].get_app_info().should_show()) | ||||
| @@ -1064,7 +1066,7 @@ const AppSearchProvider = new Lang.Class({ | ||||
|  | ||||
|     getInitialResultSet: function(terms, callback, cancellable) { | ||||
|         let query = terms.join(' '); | ||||
|         let groups = Gio.DesktopAppInfo.search(query); | ||||
|         let groups = Shell.AppSystem.search(query); | ||||
|         let usage = Shell.AppUsage.get_default(); | ||||
|         let results = []; | ||||
|         groups.forEach(function(group) { | ||||
| @@ -1293,7 +1295,10 @@ const FolderIcon = new Lang.Class({ | ||||
|             if (!_listsIntersect(folderCategories, appCategories)) | ||||
|                 return; | ||||
|  | ||||
|             addAppId(appInfo.get_id()); | ||||
|             try { | ||||
|                 addAppId(appInfo.get_id()); // catch invalid file encodings | ||||
|             } catch(e) { | ||||
|             } | ||||
|         }); | ||||
|  | ||||
|         this.actor.visible = this.view.getAllItems().length > 0; | ||||
| @@ -1804,8 +1809,6 @@ const AppIconMenu = new Lang.Class({ | ||||
|  | ||||
|         this.actor.add_style_class_name('app-well-menu'); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); | ||||
|  | ||||
|         // Chain our visibility and lifecycle to that of the source | ||||
|         source.actor.connect('notify::mapped', Lang.bind(this, function () { | ||||
|             if (!source.actor.mapped) | ||||
| @@ -1867,7 +1870,7 @@ const AppIconMenu = new Lang.Class({ | ||||
|                 })); | ||||
|             } | ||||
|  | ||||
|             let canFavorite = this._settings.is_writable('favorite-apps'); | ||||
|             let canFavorite = global.settings.is_writable('favorite-apps'); | ||||
|  | ||||
|             if (canFavorite) { | ||||
|                 this._appendSeparator(); | ||||
|   | ||||
| @@ -26,6 +26,7 @@ const RENAMED_DESKTOP_IDS = { | ||||
|     'gnome-photos.desktop': 'org.gnome.Photos.desktop', | ||||
|     'gnome-screenshot.desktop': 'org.gnome.Screenshot.desktop', | ||||
|     'gnome-software.desktop': 'org.gnome.Software.desktop', | ||||
|     'gnome-terminal.desktop': 'org.gnome.Terminal.desktop', | ||||
|     'gnome-weather.desktop': 'org.gnome.Weather.Application.desktop', | ||||
|     'gnomine.desktop': 'gnome-mines.desktop', | ||||
|     'gnotravex.desktop': 'gnome-tetravex.desktop', | ||||
|   | ||||
| @@ -144,6 +144,7 @@ const BackgroundCache = new Lang.Class({ | ||||
|         this._pendingFileLoads = []; | ||||
|         this._fileMonitors = {}; | ||||
|         this._backgroundSources = {}; | ||||
|         this._animations = {}; | ||||
|     }, | ||||
|  | ||||
|     monitorFile: function(file) { | ||||
| @@ -162,12 +163,13 @@ const BackgroundCache = new Lang.Class({ | ||||
|  | ||||
|     getAnimation: function(params) { | ||||
|         params = Params.parse(params, { file: null, | ||||
|                                         settingsSchema: null, | ||||
|                                         onLoaded: null }); | ||||
|  | ||||
|         if (_fileEqual0(this._animationFile, params.file)) { | ||||
|         if (this._animations[params.settingsSchema] && _fileEqual0(this._animationFile, params.file)) { | ||||
|             if (params.onLoaded) { | ||||
|                 let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { | ||||
|                     params.onLoaded(this._animation); | ||||
|                     params.onLoaded(this._animations[params.settingsSchema]); | ||||
|                     return GLib.SOURCE_REMOVE; | ||||
|                 })); | ||||
|                 GLib.Source.set_name_by_id(id, '[gnome-shell] params.onLoaded'); | ||||
| @@ -178,12 +180,11 @@ const BackgroundCache = new Lang.Class({ | ||||
|         let animation = new Animation({ file: params.file }); | ||||
|  | ||||
|         animation.load(Lang.bind(this, function() { | ||||
|                            this._animationFile = params.file; | ||||
|                            this._animation = animation; | ||||
|                            this._animations[params.settingsSchema] = animation; | ||||
|  | ||||
|                            if (params.onLoaded) { | ||||
|                                let id = GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { | ||||
|                                    params.onLoaded(this._animation); | ||||
|                                    params.onLoaded(this._animations[params.settingsSchema]); | ||||
|                                    return GLib.SOURCE_REMOVE; | ||||
|                                })); | ||||
|                                GLib.Source.set_name_by_id(id, '[gnome-shell] params.onLoaded'); | ||||
| @@ -246,6 +247,13 @@ const Background = new Lang.Class({ | ||||
|         this._cancellable = new Gio.Cancellable(); | ||||
|         this.isLoaded = false; | ||||
|  | ||||
|         this._clock = new GnomeDesktop.WallClock(); | ||||
|         this._timezoneChangedId = this._clock.connect('notify::timezone', | ||||
|             Lang.bind(this, function() { | ||||
|                 if (this._animation) | ||||
|                     this._loadAnimation(this._animation.file); | ||||
|             })); | ||||
|  | ||||
|         this._settingsChangedSignalId = this._settings.connect('changed', Lang.bind(this, function() { | ||||
|                                             this.emit('changed'); | ||||
|                                         })); | ||||
| @@ -264,6 +272,10 @@ const Background = new Lang.Class({ | ||||
|         } | ||||
|         this._fileWatches = null; | ||||
|  | ||||
|         if (this._timezoneChangedId != 0) | ||||
|             this._clock.disconnect(this._timezoneChangedId); | ||||
|         this._timezoneChangedId = 0; | ||||
|  | ||||
|         if (this._settingsChangedSignalId != 0) | ||||
|             this._settings.disconnect(this._settingsChangedSignalId); | ||||
|         this._settingsChangedSignalId = 0; | ||||
| @@ -403,17 +415,18 @@ const Background = new Lang.Class({ | ||||
|  | ||||
|     _loadAnimation: function(file) { | ||||
|         this._cache.getAnimation({ file: file, | ||||
|                                          onLoaded: Lang.bind(this, function(animation) { | ||||
|                                              this._animation = animation; | ||||
|                                    settingsSchema: this._settings.schema_id, | ||||
|                                    onLoaded: Lang.bind(this, function(animation) { | ||||
|                                        this._animation = animation; | ||||
|  | ||||
|                                              if (!this._animation || this._cancellable.is_cancelled()) { | ||||
|                                                  this._setLoaded(); | ||||
|                                                  return; | ||||
|                                              } | ||||
|                                        if (!this._animation || this._cancellable.is_cancelled()) { | ||||
|                                            this._setLoaded(); | ||||
|                                            return; | ||||
|                                        } | ||||
|  | ||||
|                                              this._updateAnimation(); | ||||
|                                              this._watchFile(file); | ||||
|                                          }) | ||||
|                                        this._updateAnimation(); | ||||
|                                        this._watchFile(file); | ||||
|                                    }) | ||||
|                                  }); | ||||
|     }, | ||||
|  | ||||
| @@ -527,6 +540,10 @@ const BackgroundSource = new Lang.Class({ | ||||
|         let file = null; | ||||
|         let style; | ||||
|  | ||||
|         // We don't watch changes to settings here, | ||||
|         // instead we rely on Background to watch those | ||||
|         // and emit 'changed' at the right time | ||||
|  | ||||
|         if (this._overrideImage != null) { | ||||
|             file = Gio.File.new_for_path(this._overrideImage); | ||||
|             style = GDesktopEnums.BackgroundStyle.ZOOM; // Hardcode | ||||
|   | ||||
| @@ -16,9 +16,10 @@ const BackgroundMenu = new Lang.Class({ | ||||
|     _init: function(layoutManager) { | ||||
|         this.parent(layoutManager.dummyCursor, 0, St.Side.TOP); | ||||
|  | ||||
|         this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop'); | ||||
|         this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this.addSettingsAction(_("Change Background…"), 'gnome-background-panel.desktop'); | ||||
|         this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this.addSettingsAction(_("Display Settings"), 'gnome-display-panel.desktop'); | ||||
|         this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop'); | ||||
|  | ||||
|         this.actor.add_style_class_name('background-menu'); | ||||
|  | ||||
|   | ||||
| @@ -24,6 +24,8 @@ const MSECS_IN_DAY = 24 * 60 * 60 * 1000; | ||||
| const SHOW_WEEKDATE_KEY = 'show-weekdate'; | ||||
| const ELLIPSIS_CHAR = '\u2026'; | ||||
|  | ||||
| const MESSAGE_ICON_SIZE = 32; | ||||
|  | ||||
| const MESSAGE_ANIMATION_TIME = 0.1; | ||||
|  | ||||
| const DEFAULT_EXPAND_LINES = 6; | ||||
| @@ -44,6 +46,10 @@ function _sameDay(dateA, dateB) { | ||||
|     return _sameMonth(dateA, dateB) && (dateA.getDate() == dateB.getDate()); | ||||
| } | ||||
|  | ||||
| function _isToday(date) { | ||||
|     return _sameDay(new Date(), date); | ||||
| } | ||||
|  | ||||
| function _isWorkDay(date) { | ||||
|     /* Translators: Enter 0-6 (Sunday-Saturday) for non-work days. Examples: "0" (Sunday) "6" (Saturday) "06" (Sunday and Saturday). */ | ||||
|     let days = C_('calendar-no-work', "06"); | ||||
| @@ -68,21 +74,6 @@ function _getEndOfDay(date) { | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| function _formatEventTime(event, periodBegin, periodEnd) { | ||||
|     let ret; | ||||
|     let allDay = (event.allDay || (event.date <= periodBegin && event.end >= periodEnd)); | ||||
|     if (allDay) { | ||||
|         /* Translators: Shown in calendar event list for all day events | ||||
|          * Keep it short, best if you can use less then 10 characters | ||||
|          */ | ||||
|         ret = C_("event list time", "All Day"); | ||||
|     } else { | ||||
|         let date = event.date >= periodBegin ? event.date : event.end; | ||||
|         ret = Util.formatTime(date, { timeOnly: true }); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
|  | ||||
| function _getCalendarDayAbbreviation(dayNumber) { | ||||
|     let abbreviations = [ | ||||
|         /* Translators: Calendar grid abbreviation for Sunday. | ||||
| @@ -133,7 +124,8 @@ const URLHighlighter = new Lang.Class({ | ||||
|     _init: function(text, lineWrap, allowMarkup) { | ||||
|         if (!text) | ||||
|             text = ''; | ||||
|         this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter' }); | ||||
|         this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter', | ||||
|                                     x_expand: true, x_align: Clutter.ActorAlign.START }); | ||||
|         this._linkColor = '#ccccff'; | ||||
|         this.actor.connect('style-changed', Lang.bind(this, function() { | ||||
|             let [hasColor, color] = this.actor.get_theme_node().lookup_color('link-color', false); | ||||
| @@ -253,7 +245,8 @@ const URLHighlighter = new Lang.Class({ | ||||
| const CalendarEvent = new Lang.Class({ | ||||
|     Name: 'CalendarEvent', | ||||
|  | ||||
|     _init: function(date, end, summary, allDay) { | ||||
|     _init: function(id, date, end, summary, allDay) { | ||||
|         this.id = id; | ||||
|         this.date = date; | ||||
|         this.end = end; | ||||
|         this.summary = summary; | ||||
| @@ -425,9 +418,10 @@ const DBusEventSource = new Lang.Class({ | ||||
|                 let a = appointments[n]; | ||||
|                 let date = new Date(a[4] * 1000); | ||||
|                 let end = new Date(a[5] * 1000); | ||||
|                 let id = a[0]; | ||||
|                 let summary = a[1]; | ||||
|                 let allDay = a[3]; | ||||
|                 let event = new CalendarEvent(date, end, summary, allDay); | ||||
|                 let event = new CalendarEvent(id, date, end, summary, allDay); | ||||
|                 newEvents.push(event); | ||||
|             } | ||||
|             newEvents.sort(function(event1, event2) { | ||||
| @@ -731,7 +725,8 @@ const Calendar = new Lang.Class({ | ||||
|         // nRows here means 6 weeks + one header + one navbar | ||||
|         let nRows = 8; | ||||
|         while (row < 8) { | ||||
|             let button = new St.Button({ label: iter.getDate().toString(), | ||||
|             // xgettext:no-javascript-format | ||||
|             let button = new St.Button({ label: iter.toLocaleFormat(C_("date day number format", "%d")), | ||||
|                                          can_focus: true }); | ||||
|             let rtl = button.get_text_direction() == Clutter.TextDirection.RTL; | ||||
|  | ||||
| @@ -984,7 +979,6 @@ const Message = new Lang.Class({ | ||||
|         this._iconBin = new St.Bin({ style_class: 'message-icon-bin', | ||||
|                                      y_expand: true, | ||||
|                                      visible: false }); | ||||
|         this._iconBin.set_y_align(Clutter.ActorAlign.START); | ||||
|         hbox.add_actor(this._iconBin); | ||||
|  | ||||
|         let contentBox = new St.BoxLayout({ style_class: 'message-content', | ||||
| @@ -1008,8 +1002,7 @@ const Message = new Lang.Class({ | ||||
|         this._closeButton = new St.Button({ child: closeIcon, visible: false }); | ||||
|         titleBox.add_actor(this._closeButton); | ||||
|  | ||||
|         this._bodyStack = new St.Widget({ x_expand: true, | ||||
|                                           x_align: Clutter.ActorAlign.START }); | ||||
|         this._bodyStack = new St.Widget({ x_expand: true }); | ||||
|         this._bodyStack.layout_manager = new LabelExpanderLayout(); | ||||
|         contentBox.add_actor(this._bodyStack); | ||||
|  | ||||
| @@ -1018,16 +1011,17 @@ const Message = new Lang.Class({ | ||||
|         this._bodyStack.add_actor(this.bodyLabel.actor); | ||||
|         this.setBody(body); | ||||
|  | ||||
|         this._closeButton.connect('clicked', Lang.bind(this, | ||||
|             function() { | ||||
|                 this.emit('close'); | ||||
|             })); | ||||
|         this._closeButton.connect('clicked', Lang.bind(this, this.close)); | ||||
|         this.actor.connect('notify::hover', Lang.bind(this, this._sync)); | ||||
|         this.actor.connect('clicked', Lang.bind(this, this._onClicked)); | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     close: function() { | ||||
|         this.emit('close'); | ||||
|     }, | ||||
|  | ||||
|     setIcon: function(actor) { | ||||
|         this._iconBin.child = actor; | ||||
|         this._iconBin.visible = (actor != null); | ||||
| @@ -1044,7 +1038,10 @@ const Message = new Lang.Class({ | ||||
|  | ||||
|     setBody: function(text) { | ||||
|         this._bodyText = text; | ||||
|         this.bodyLabel.setMarkup(text, this._useBodyMarkup); | ||||
|         this.bodyLabel.setMarkup(text ? text.replace(/\n/g, ' ') : '', | ||||
|                                  this._useBodyMarkup); | ||||
|         if (this._expandedLabel) | ||||
|             this._expandedLabel.setMarkup(text, this._useBodyMarkup); | ||||
|     }, | ||||
|  | ||||
|     setUseBodyMarkup: function(enable) { | ||||
| @@ -1092,9 +1089,9 @@ const Message = new Lang.Class({ | ||||
|         this._actionBin.visible = (this._actionBin.get_n_children() > 0); | ||||
|  | ||||
|         if (this._bodyStack.get_n_children() < 2) { | ||||
|             let expandedLabel = new URLHighlighter(this._bodyText, | ||||
|                                                    true, this._useBodyMarkup); | ||||
|             this.setExpandedBody(expandedLabel.actor); | ||||
|             this._expandedLabel = new URLHighlighter(this._bodyText, | ||||
|                                                      true, this._useBodyMarkup); | ||||
|             this.setExpandedBody(this._expandedLabel.actor); | ||||
|         } | ||||
|  | ||||
|         if (animate) { | ||||
| @@ -1111,6 +1108,8 @@ const Message = new Lang.Class({ | ||||
|             this._bodyStack.layout_manager.expansion = 1; | ||||
|             this._actionBin.scale_y = 1; | ||||
|         } | ||||
|  | ||||
|         this.emit('expanded'); | ||||
|     }, | ||||
|  | ||||
|     unexpand: function(animate) { | ||||
| @@ -1133,15 +1132,17 @@ const Message = new Lang.Class({ | ||||
|             this._actionBin.scale_y = 0; | ||||
|             this.expanded = false; | ||||
|         } | ||||
|  | ||||
|         this.emit('unexpanded'); | ||||
|     }, | ||||
|  | ||||
|     canClear: function() { | ||||
|     canClose: function() { | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         let hovered = this.actor.hover; | ||||
|         this._closeButton.visible = hovered; | ||||
|         this._closeButton.visible = hovered && this.canClose(); | ||||
|         this._secondaryBin.visible = !hovered; | ||||
|     }, | ||||
|  | ||||
| @@ -1156,7 +1157,7 @@ const Message = new Lang.Class({ | ||||
|  | ||||
|         if (keysym == Clutter.KEY_Delete || | ||||
|             keysym == Clutter.KEY_KP_Delete) { | ||||
|             this.emit('close'); | ||||
|             this.close(); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
| @@ -1164,6 +1165,55 @@ const Message = new Lang.Class({ | ||||
| }); | ||||
| Signals.addSignalMethods(Message.prototype); | ||||
|  | ||||
| const EventMessage = new Lang.Class({ | ||||
|     Name: 'EventMessage', | ||||
|     Extends: Message, | ||||
|  | ||||
|     _init: function(event, date) { | ||||
|         this._event = event; | ||||
|         this._date = date; | ||||
|  | ||||
|         this.parent(this._formatEventTime(), event.summary); | ||||
|     }, | ||||
|  | ||||
|     _formatEventTime: function() { | ||||
|         let periodBegin = _getBeginningOfDay(this._date); | ||||
|         let periodEnd = _getEndOfDay(this._date); | ||||
|         let allDay = (this._event.allDay || (this._event.date <= periodBegin && | ||||
|                                              this._event.end >= periodEnd)); | ||||
|         let title; | ||||
|         if (allDay) { | ||||
|             /* Translators: Shown in calendar event list for all day events | ||||
|              * Keep it short, best if you can use less then 10 characters | ||||
|              */ | ||||
|             title = C_("event list time", "All Day"); | ||||
|         } else { | ||||
|             let date = this._event.date >= periodBegin ? this._event.date | ||||
|                                                        : this._event.end; | ||||
|             title = Util.formatTime(date, { timeOnly: true }); | ||||
|         } | ||||
|  | ||||
|         let rtl = Clutter.get_default_text_direction() == Clutter.TextDirection.RTL; | ||||
|         if (this._event.date < periodBegin && !this._event.allDay) { | ||||
|             if (rtl) | ||||
|                 title = title + ELLIPSIS_CHAR; | ||||
|             else | ||||
|                 title = ELLIPSIS_CHAR + title; | ||||
|         } | ||||
|         if (this._event.end > periodEnd && !this._event.allDay) { | ||||
|             if (rtl) | ||||
|                 title = ELLIPSIS_CHAR + title; | ||||
|             else | ||||
|                 title = title + ELLIPSIS_CHAR; | ||||
|         } | ||||
|         return title; | ||||
|     }, | ||||
|  | ||||
|     canClose: function() { | ||||
|         return _isToday(this._date); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const NotificationMessage = new Lang.Class({ | ||||
|     Name: 'NotificationMessage', | ||||
|     Extends: Message, | ||||
| @@ -1181,10 +1231,10 @@ const NotificationMessage = new Lang.Class({ | ||||
|                 this._closed = true; | ||||
|                 this.notification.destroy(MessageTray.NotificationDestroyedReason.DISMISSED); | ||||
|             })); | ||||
|         notification.connect('destroy', Lang.bind(this, | ||||
|         this._destroyId = notification.connect('destroy', Lang.bind(this, | ||||
|             function() { | ||||
|                 if (!this._closed) | ||||
|                     this.emit('close'); | ||||
|                     this.close(); | ||||
|             })); | ||||
|         this._updatedId = notification.connect('updated', | ||||
|                                                Lang.bind(this, this._onUpdated)); | ||||
| @@ -1192,9 +1242,10 @@ const NotificationMessage = new Lang.Class({ | ||||
|  | ||||
|     _getIcon: function() { | ||||
|         if (this.notification.gicon) | ||||
|             return new St.Icon({ gicon: this.notification.gicon, icon_size: 48 }); | ||||
|             return new St.Icon({ gicon: this.notification.gicon, | ||||
|                                  icon_size: MESSAGE_ICON_SIZE }); | ||||
|         else | ||||
|             return this.notification.source.createIcon(48); | ||||
|             return this.notification.source.createIcon(MESSAGE_ICON_SIZE); | ||||
|     }, | ||||
|  | ||||
|     _onUpdated: function(n, clear) { | ||||
| @@ -1204,10 +1255,6 @@ const NotificationMessage = new Lang.Class({ | ||||
|         this.setUseBodyMarkup(n.bannerBodyMarkup); | ||||
|     }, | ||||
|  | ||||
|     canClear: function() { | ||||
|         return !this.notification.resident; | ||||
|     }, | ||||
|  | ||||
|     _onClicked: function() { | ||||
|         this.notification.activate(); | ||||
|     }, | ||||
| @@ -1216,6 +1263,10 @@ const NotificationMessage = new Lang.Class({ | ||||
|         if (this._updatedId) | ||||
|             this.notification.disconnect(this._updatedId); | ||||
|         this._updatedId = 0; | ||||
|  | ||||
|         if (this._destroyId) | ||||
|             this.notification.disconnect(this._destroyId); | ||||
|         this._destroyId = 0; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -1289,7 +1340,7 @@ const MessageListSection = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     addMessage: function(message, animate) { | ||||
|         this.addMessageAtIndex(message, 0, animate); | ||||
|         this.addMessageAtIndex(message, -1, animate); | ||||
|     }, | ||||
|  | ||||
|     addMessageAtIndex: function(message, index, animate) { | ||||
| @@ -1358,26 +1409,30 @@ const MessageListSection = new Lang.Class({ | ||||
|  | ||||
|         this._messages.delete(message); | ||||
|  | ||||
|         if (animate) | ||||
|         if (animate) { | ||||
|             Tweener.addTween(obj.container, { scale_x: 0, scale_y: 0, | ||||
|                                               time: MESSAGE_ANIMATION_TIME, | ||||
|                                               transition: 'easeOutQuad', | ||||
|                                               onComplete: function() { | ||||
|                                                   obj.container.destroy(); | ||||
|                                                   global.sync_pointer(); | ||||
|                                               }}); | ||||
|         else | ||||
|         } else { | ||||
|             obj.container.destroy(); | ||||
|             global.sync_pointer(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     clear: function() { | ||||
|         let messages = [...this._messages.keys()].filter(function(message) { | ||||
|             return message.canClear(); | ||||
|             return message.canClose(); | ||||
|         }); | ||||
|  | ||||
|         // If there are few messages, letting them all zoom out looks OK | ||||
|         if (messages.length < 2) { | ||||
|             messages.forEach(Lang.bind(this, function(message) { | ||||
|                 this.removeMessage(message, true); })); | ||||
|             messages.forEach(function(message) { | ||||
|                 message.close(); | ||||
|             }); | ||||
|         } else { | ||||
|             // Otherwise we slide them out one by one, and then zoom them | ||||
|             // out "off-screen" in the end to smoothly shrink the parent | ||||
| @@ -1391,25 +1446,20 @@ const MessageListSection = new Lang.Class({ | ||||
|                                    time: MESSAGE_ANIMATION_TIME, | ||||
|                                    delay: i * delay, | ||||
|                                    transition: 'easeOutQuad', | ||||
|                                    onComplete: Lang.bind(this, function() { | ||||
|                                        this.removeMessage(message, true); | ||||
|                                    })}); | ||||
|                                    onComplete: function() { | ||||
|                                        message.close(); | ||||
|                                    }}); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _canClear: function() { | ||||
|         for (let message of this._messages.keys()) | ||||
|             if (message.canClear()) | ||||
|             if (message.canClose()) | ||||
|                 return true; | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _isToday: function() { | ||||
|         let today = new Date(); | ||||
|         return _sameDay(this._date, today); | ||||
|     }, | ||||
|  | ||||
|     _shouldShow: function() { | ||||
|         return !this.empty; | ||||
|     }, | ||||
| @@ -1437,6 +1487,15 @@ const EventsSection = new Lang.Class({ | ||||
|         this._desktopSettings.connect('changed', Lang.bind(this, this._reloadEvents)); | ||||
|         this._eventSource = new EmptyEventSource(); | ||||
|  | ||||
|         this._ignoredEvents = new Map(); | ||||
|  | ||||
|         let savedState = global.get_persistent_state('as', 'ignored_events'); | ||||
|         if (savedState) | ||||
|             savedState.deep_unpack().forEach(Lang.bind(this, | ||||
|                 function(eventId) { | ||||
|                     this._ignoredEvents.set(eventId, true); | ||||
|                 })); | ||||
|  | ||||
|         this.parent(''); | ||||
|  | ||||
|         Shell.AppSystem.get_default().connect('installed-changed', | ||||
| @@ -1444,6 +1503,12 @@ const EventsSection = new Lang.Class({ | ||||
|         this._appInstalledChanged(); | ||||
|     }, | ||||
|  | ||||
|     _ignoreEvent: function(event) { | ||||
|         this._ignoredEvents.set(event.id, true); | ||||
|         let savedState = new GLib.Variant('as', [...this._ignoredEvents.keys()]); | ||||
|         global.set_persistent_state('ignored_events', savedState); | ||||
|     }, | ||||
|  | ||||
|     setEventSource: function(eventSource) { | ||||
|         this._eventSource = eventSource; | ||||
|         this._eventSource.connect('changed', Lang.bind(this, this._reloadEvents)); | ||||
| @@ -1454,13 +1519,13 @@ const EventsSection = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _updateTitle: function() { | ||||
|         let now = new Date(); | ||||
|         if (_sameDay(this._date, now)) { | ||||
|         if (_isToday(this._date)) { | ||||
|             this._title.label = _("Events"); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let dayFormat; | ||||
|         let now = new Date(); | ||||
|         if (_sameYear(this._date, now)) | ||||
|             /* Translators: Shown on calendar heading when selected day occurs on current year */ | ||||
|             dayFormat = Shell.util_translate_time_string(NC_("calendar heading", | ||||
| @@ -1486,22 +1551,15 @@ const EventsSection = new Lang.Class({ | ||||
|  | ||||
|         for (let i = 0; i < events.length; i++) { | ||||
|             let event = events[i]; | ||||
|             let title = _formatEventTime(event, periodBegin, periodEnd); | ||||
|  | ||||
|             let rtl = this.actor.get_text_direction() == Clutter.TextDirection.RTL; | ||||
|             if (event.date < periodBegin && !event.allDay) { | ||||
|                 if (rtl) | ||||
|                     title = title + ELLIPSIS_CHAR; | ||||
|                 else | ||||
|                     title = ELLIPSIS_CHAR + title; | ||||
|             } | ||||
|             if (event.end > periodEnd && !event.allDay) { | ||||
|                 if (rtl) | ||||
|                     title = ELLIPSIS_CHAR + title; | ||||
|                 else | ||||
|                     title = title + ELLIPSIS_CHAR; | ||||
|             } | ||||
|             this.addMessage(new Message(title, event.summary), false); | ||||
|             if (this._ignoredEvents.has(event.id)) | ||||
|                 continue; | ||||
|  | ||||
|             let message = new EventMessage(event, this._date); | ||||
|             message.connect('close', Lang.bind(this, function() { | ||||
|                 this._ignoreEvent(event); | ||||
|             })); | ||||
|             this.addMessage(message, false); | ||||
|         } | ||||
|  | ||||
|         this._reloading = false; | ||||
| @@ -1544,7 +1602,7 @@ const EventsSection = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _shouldShow: function() { | ||||
|         return !this.empty || !this._isToday(); | ||||
|         return !this.empty || !_isToday(this._date); | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
| @@ -1663,7 +1721,7 @@ const NotificationSection = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _shouldShow: function() { | ||||
|         return !this.empty && this._isToday(); | ||||
|         return !this.empty && _isToday(this._date); | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
| @@ -1703,7 +1761,7 @@ const Placeholder = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         let isToday = _sameDay(this._date, new Date()); | ||||
|         let isToday = _isToday(this._date); | ||||
|         if (isToday && this._icon.gicon == this._todayIcon) | ||||
|             return; | ||||
|         if (!isToday && this._icon.gicon == this._otherIcon) | ||||
|   | ||||
| @@ -313,6 +313,10 @@ const AutorunSource = new Lang.Class({ | ||||
|  | ||||
|     getIcon: function() { | ||||
|         return this.mount.get_icon(); | ||||
|     }, | ||||
|  | ||||
|     _createPolicy: function() { | ||||
|         return new MessageTray.NotificationApplicationPolicy('org.gnome.Nautilus'); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -325,9 +329,6 @@ const AutorunNotification = new Lang.Class({ | ||||
|  | ||||
|         this._manager = manager; | ||||
|         this._mount = source.mount; | ||||
|  | ||||
|         // set the notification to urgent, so that it expands out | ||||
|         this.setUrgency(MessageTray.Urgency.CRITICAL); | ||||
|     }, | ||||
|  | ||||
|     createBanner: function() { | ||||
|   | ||||
| @@ -9,9 +9,15 @@ const Gio = imports.gi.Gio; | ||||
| const GObject = imports.gi.GObject; | ||||
| const Gcr = imports.gi.Gcr; | ||||
|  | ||||
| const Animation = imports.ui.animation; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
| const CheckBox = imports.ui.checkBox; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const WORK_SPINNER_ICON_SIZE = 16; | ||||
| const WORK_SPINNER_ANIMATION_DELAY = 1.0; | ||||
| const WORK_SPINNER_ANIMATION_TIME = 0.3; | ||||
|  | ||||
| const KeyringDialog = new Lang.Class({ | ||||
|     Name: 'KeyringDialog', | ||||
| @@ -58,27 +64,47 @@ const KeyringDialog = new Lang.Class({ | ||||
|                             { y_fill:  true, | ||||
|                               y_align: St.Align.START }); | ||||
|  | ||||
|         this._workSpinner = null; | ||||
|         this._controlTable = null; | ||||
|  | ||||
|  | ||||
|         this._cancelButton = this.addButton({ label: '', | ||||
|                                               action: Lang.bind(this, this._onCancelButton), | ||||
|                                               key: Clutter.Escape }, | ||||
|                                             { expand: true, x_fill: false, x_align: St.Align.START }); | ||||
|         this.placeSpinner({ expand: false, | ||||
|                             x_fill: false, | ||||
|                             y_fill: false, | ||||
|                             x_align: St.Align.END, | ||||
|                             y_align: St.Align.MIDDLE }); | ||||
|                                               key: Clutter.Escape }); | ||||
|         this._continueButton = this.addButton({ label: '', | ||||
|                                                 action: Lang.bind(this, this._onContinueButton), | ||||
|                                                 default: true }, | ||||
|                                               { expand: false, x_fill: false, x_align: St.Align.END }); | ||||
|                                                 default: true }); | ||||
|  | ||||
|         this.prompt.bind_property('cancel-label', this._cancelButton, 'label', GObject.BindingFlags.SYNC_CREATE); | ||||
|         this.prompt.bind_property('continue-label', this._continueButton, 'label', GObject.BindingFlags.SYNC_CREATE); | ||||
|     }, | ||||
|  | ||||
|     _setWorking: function(working) { | ||||
|         if (!this._workSpinner) | ||||
|             return; | ||||
|  | ||||
|         Tweener.removeTweens(this._workSpinner.actor); | ||||
|         if (working) { | ||||
|             this._workSpinner.play(); | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 255, | ||||
|                                delay: WORK_SPINNER_ANIMATION_DELAY, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear' | ||||
|                              }); | ||||
|         } else { | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 0, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear', | ||||
|                                onCompleteScope: this, | ||||
|                                onComplete: function() { | ||||
|                                    if (this._workSpinner) | ||||
|                                        this._workSpinner.stop(); | ||||
|                                } | ||||
|                              }); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _buildControlTable: function() { | ||||
|         let layout = new Clutter.GridLayout({ orientation: Clutter.Orientation.VERTICAL }); | ||||
|         let table = new St.Widget({ style_class: 'keyring-dialog-control-table', | ||||
| @@ -101,15 +127,22 @@ const KeyringDialog = new Lang.Class({ | ||||
|             ShellEntry.addContextMenu(this._passwordEntry, { isPassword: true }); | ||||
|             this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onPasswordActivate)); | ||||
|  | ||||
|             let spinnerIcon = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg'); | ||||
|             this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE); | ||||
|             this._workSpinner.actor.opacity = 0; | ||||
|  | ||||
|             if (rtl) { | ||||
|                 layout.attach(this._passwordEntry, 0, row, 1, 1); | ||||
|                 layout.attach(label, 1, row, 1, 1); | ||||
|                 layout.attach(this._workSpinner.actor, 0, row, 1, 1); | ||||
|                 layout.attach(this._passwordEntry, 1, row, 1, 1); | ||||
|                 layout.attach(label, 2, row, 1, 1); | ||||
|             } else { | ||||
|                 layout.attach(label, 0, row, 1, 1); | ||||
|                 layout.attach(this._passwordEntry, 1, row, 1, 1); | ||||
|                 layout.attach(this._workSpinner.actor, 2, row, 1, 1); | ||||
|             } | ||||
|             row++; | ||||
|         } else { | ||||
|             this._workSpinner = null; | ||||
|             this._passwordEntry = null; | ||||
|         } | ||||
|  | ||||
| @@ -178,7 +211,7 @@ const KeyringDialog = new Lang.Class({ | ||||
|  | ||||
|         this._continueButton.can_focus = sensitive; | ||||
|         this._continueButton.reactive = sensitive; | ||||
|         this.setWorking(!sensitive); | ||||
|         this._setWorking(!sensitive); | ||||
|     }, | ||||
|  | ||||
|     _ensureOpen: function() { | ||||
|   | ||||
| @@ -12,6 +12,8 @@ const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const Config = imports.misc.config; | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
| @@ -338,6 +340,7 @@ const NetworkSecretDialog = new Lang.Class({ | ||||
|                 content.message = _("PIN code is needed for the mobile broadband device"); | ||||
|                 content.secrets.push({ label: _("PIN: "), key: 'pin', | ||||
|                                        value: gsmSetting.pin || '', password: true }); | ||||
|                 break; | ||||
|             } | ||||
|             // fall through | ||||
|         case 'cdma': | ||||
| @@ -610,6 +613,7 @@ const NetworkAgent = new Lang.Class({ | ||||
|  | ||||
|         this._dialogs = { }; | ||||
|         this._vpnRequests = { }; | ||||
|         this._notifications = { }; | ||||
|  | ||||
|         this._native.connect('new-request', Lang.bind(this, this._newRequest)); | ||||
|         this._native.connect('cancel-request', Lang.bind(this, this._cancelRequest)); | ||||
| @@ -632,21 +636,92 @@ const NetworkAgent = new Lang.Class({ | ||||
|             this._vpnRequests[requestId].cancel(true); | ||||
|         this._vpnRequests = { }; | ||||
|  | ||||
|         for (requestId in this._notifications) | ||||
|             this._notifications[requestId].destroy(); | ||||
|         this._notifications = { }; | ||||
|  | ||||
|         this._enabled = false; | ||||
|     }, | ||||
|  | ||||
|     _showNotification: function(requestId, connection, settingName, hints, flags) { | ||||
|         let source = new MessageTray.Source(_("Network Manager"), 'network-transmit-receive'); | ||||
|         source.policy = new MessageTray.NotificationApplicationPolicy('gnome-network-panel'); | ||||
|  | ||||
|         let title, body; | ||||
|  | ||||
|         let connectionSetting = connection.get_setting_connection(); | ||||
|         let connectionType = connectionSetting.get_connection_type(); | ||||
|         switch (connectionType) { | ||||
|         case '802-11-wireless': | ||||
|             let wirelessSetting = connection.get_setting_wireless(); | ||||
|             let ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid()); | ||||
|             title = _("Authentication required by wireless network"); | ||||
|             body = _("Passwords or encryption keys are required to access the wireless network “%s”.").format(ssid); | ||||
|             break; | ||||
|         case '802-3-ethernet': | ||||
|             title = _("Wired 802.1X authentication"); | ||||
|             body = _("A password is required to connect to “%s”.".format(connection.get_id())); | ||||
|             break; | ||||
|         case 'pppoe': | ||||
|             title = _("DSL authentication"); | ||||
|             body = _("A password is required to connect to “%s”.".format(connection.get_id())); | ||||
|             break; | ||||
|         case 'gsm': | ||||
|             if (hints.indexOf('pin') != -1) { | ||||
|                 let gsmSetting = connection.get_setting_gsm(); | ||||
|                 title = _("PIN code required"); | ||||
|                 message = _("PIN code is needed for the mobile broadband device"); | ||||
|                 break; | ||||
|             } | ||||
|             // fall through | ||||
|         case 'cdma': | ||||
|         case 'bluetooth': | ||||
|             title = _("Mobile broadband network password"); | ||||
|             message = _("A password is required to connect to “%s”.").format(connectionSetting.get_id()); | ||||
|             break; | ||||
|         default: | ||||
|             log('Invalid connection type: ' + connectionType); | ||||
|             this._native.respond(requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let notification = new MessageTray.Notification(source, title, body); | ||||
|  | ||||
|         notification.connect('activated', Lang.bind(this, function() { | ||||
|             notification.answered = true; | ||||
|             this._handleRequest(requestId, connection, settingName, hints, flags); | ||||
|         })); | ||||
|  | ||||
|         this._notifications[requestId] = notification; | ||||
|         notification.connect('destroy', Lang.bind(this, function() { | ||||
|             if (!notification.answered) | ||||
|                 this._native.respond(requestId, Shell.NetworkAgentResponse.USER_CANCELED); | ||||
|             delete this._notifications[requestId]; | ||||
|         })); | ||||
|  | ||||
|         Main.messageTray.add(source); | ||||
|         source.notify(notification); | ||||
|     }, | ||||
|  | ||||
|     _newRequest:  function(agent, requestId, connection, settingName, hints, flags) { | ||||
|         if (!this._enabled) { | ||||
|             agent.respond(requestId, Shell.NetworkAgentResponse.USER_CANCELED); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         if (!(flags & NMClient.SecretAgentGetSecretsFlags.USER_REQUESTED)) | ||||
|             this._showNotification(requestId, connection, settingName, hints, flags); | ||||
|         else | ||||
|             this._handleRequest(requestId, connection, settingName, hints, flags); | ||||
|     }, | ||||
|  | ||||
|     _handleRequest: function(requestId, connection, settingName, hints, flags) { | ||||
|         if (settingName == 'vpn') { | ||||
|             this._vpnRequest(requestId, connection, hints, flags); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let dialog = new NetworkSecretDialog(agent, requestId, connection, settingName, hints); | ||||
|         let dialog = new NetworkSecretDialog(this._native, requestId, connection, settingName, hints); | ||||
|         dialog.connect('destroy', Lang.bind(this, function() { | ||||
|             delete this._dialogs[requestId]; | ||||
|         })); | ||||
|   | ||||
| @@ -13,13 +13,19 @@ const Mainloop = imports.mainloop; | ||||
| const Polkit = imports.gi.Polkit; | ||||
| const PolkitAgent = imports.gi.PolkitAgent; | ||||
|  | ||||
| const Animation = imports.ui.animation; | ||||
| const Components = imports.ui.components; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const ShellEntry = imports.ui.shellEntry; | ||||
| const UserWidget = imports.ui.userWidget; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const DIALOG_ICON_SIZE = 48; | ||||
|  | ||||
| const WORK_SPINNER_ICON_SIZE = 16; | ||||
| const WORK_SPINNER_ANIMATION_DELAY = 1.0; | ||||
| const WORK_SPINNER_ANIMATION_TIME = 0.3; | ||||
|  | ||||
| const AuthenticationDialog = new Lang.Class({ | ||||
|     Name: 'AuthenticationDialog', | ||||
|     Extends: ModalDialog.ModalDialog, | ||||
| @@ -136,6 +142,13 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|         this._passwordEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivate)); | ||||
|         this._passwordBox.add(this._passwordEntry, | ||||
|                               { expand: true }); | ||||
|  | ||||
|         let spinnerIcon = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg'); | ||||
|         this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE); | ||||
|         this._workSpinner.actor.opacity = 0; | ||||
|  | ||||
|         this._passwordBox.add(this._workSpinner.actor); | ||||
|  | ||||
|         this.setInitialKeyFocus(this._passwordEntry); | ||||
|         this._passwordBox.hide(); | ||||
|  | ||||
| @@ -165,17 +178,10 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|  | ||||
|         this._cancelButton = this.addButton({ label: _("Cancel"), | ||||
|                                               action: Lang.bind(this, this.cancel), | ||||
|                                               key: Clutter.Escape }, | ||||
|                                             { expand: true, x_fill: false, x_align: St.Align.START }); | ||||
|         this.placeSpinner({ expand: false, | ||||
|                             x_fill: false, | ||||
|                             y_fill: false, | ||||
|                             x_align: St.Align.END, | ||||
|                             y_align: St.Align.MIDDLE }); | ||||
|                                               key: Clutter.Escape }); | ||||
|         this._okButton = this.addButton({ label:  _("Authenticate"), | ||||
|                                           action: Lang.bind(this, this._onAuthenticateButtonPressed), | ||||
|                                           default: true }, | ||||
|                                         { expand: false, x_fill: false, x_align: St.Align.END }); | ||||
|                                           default: true }); | ||||
|  | ||||
|         this._doneEmitted = false; | ||||
|  | ||||
| @@ -183,6 +189,30 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|         this._cookie = cookie; | ||||
|     }, | ||||
|  | ||||
|     _setWorking: function(working) { | ||||
|         Tweener.removeTweens(this._workSpinner.actor); | ||||
|         if (working) { | ||||
|             this._workSpinner.play(); | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 255, | ||||
|                                delay: WORK_SPINNER_ANIMATION_DELAY, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear' | ||||
|                              }); | ||||
|         } else { | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 0, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear', | ||||
|                                onCompleteScope: this, | ||||
|                                onComplete: function() { | ||||
|                                    if (this._workSpinner) | ||||
|                                        this._workSpinner.stop(); | ||||
|                                } | ||||
|                              }); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     performAuthentication: function() { | ||||
|         this.destroySession(); | ||||
|         this._session = new PolkitAgent.Session({ identity: this._identityToAuth, | ||||
| @@ -229,7 +259,7 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|  | ||||
|         this._okButton.can_focus = sensitive; | ||||
|         this._okButton.reactive = sensitive; | ||||
|         this.setWorking(!sensitive); | ||||
|         this._setWorking(!sensitive); | ||||
|     }, | ||||
|  | ||||
|     _onEntryActivate: function() { | ||||
|   | ||||
| @@ -3,6 +3,7 @@ | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Shell = imports.gi.Shell; | ||||
| @@ -31,6 +32,8 @@ const SCROLLBACK_HISTORY_LINES = 10; | ||||
| // See Notification._onEntryChanged | ||||
| const COMPOSING_STOP_TIMEOUT = 5; | ||||
|  | ||||
| const CHAT_EXPAND_LINES = 12; | ||||
|  | ||||
| const NotificationDirection = { | ||||
|     SENT: 'chat-sent', | ||||
|     RECEIVED: 'chat-received' | ||||
| @@ -155,14 +158,6 @@ const TelepathyClient = new Lang.Class({ | ||||
|         this._chatSources[channel.get_object_path()] = source; | ||||
|         source.connect('destroy', Lang.bind(this, | ||||
|                        function() { | ||||
|                            if (this._tpClient.is_handling_channel(channel)) { | ||||
|                                // The chat box has been destroyed so it can't | ||||
|                                // handle the channel any more. | ||||
|                                channel.close_async(function(src, result) { | ||||
|                                    channel.close_finish(result); | ||||
|                                }); | ||||
|                            } | ||||
|  | ||||
|                            delete this._chatSources[channel.get_object_path()]; | ||||
|                        })); | ||||
|     }, | ||||
| @@ -271,15 +266,8 @@ const ChatSource = new Lang.Class({ | ||||
|         this._channel = channel; | ||||
|         this._closedId = this._channel.connect('invalidated', Lang.bind(this, this._channelClosed)); | ||||
|  | ||||
|         this._notification = new ChatNotification(this); | ||||
|         this._notification.connect('activated', Lang.bind(this, this.open)); | ||||
|         this._notification.setUrgency(MessageTray.Urgency.HIGH); | ||||
|         this._notifyTimeoutId = 0; | ||||
|  | ||||
|         // We ack messages when the user expands the new notification or views the summary | ||||
|         // notification, in which case the notification is also expanded. | ||||
|         this._notification.connect('expanded', Lang.bind(this, this._ackMessages)); | ||||
|  | ||||
|         this._presence = contact.get_presence_type(); | ||||
|  | ||||
|         this._sentId = this._channel.connect('message-sent', Lang.bind(this, this._messageSent)); | ||||
| @@ -292,15 +280,48 @@ const ChatSource = new Lang.Class({ | ||||
|  | ||||
|         // Add ourselves as a source. | ||||
|         Main.messageTray.add(this); | ||||
|         this.pushNotification(this._notification); | ||||
|  | ||||
|         this._getLogMessages(); | ||||
|     }, | ||||
|  | ||||
|     _ensureNotification: function() { | ||||
|         if (this._notification) | ||||
|             return; | ||||
|  | ||||
|         this._notification = new ChatNotification(this); | ||||
|         this._notification.connect('activated', Lang.bind(this, this.open)); | ||||
|         this._notification.connect('updated', Lang.bind(this, | ||||
|             function() { | ||||
|                 if (this._banner && this._banner.expanded) | ||||
|                     this._ackMessages(); | ||||
|             })); | ||||
|         this._notification.connect('destroy', Lang.bind(this, | ||||
|             function() { | ||||
|                 this._notification = null; | ||||
|             })); | ||||
|         this.pushNotification(this._notification); | ||||
|     }, | ||||
|  | ||||
|     _createPolicy: function() { | ||||
|         if (this._account.protocol_name == 'irc') | ||||
|             return new MessageTray.NotificationApplicationPolicy('org.gnome.Polari'); | ||||
|         return new MessageTray.NotificationApplicationPolicy('empathy'); | ||||
|     }, | ||||
|  | ||||
|     createBanner: function() { | ||||
|         this._banner = new ChatNotificationBanner(this._notification); | ||||
|  | ||||
|         // We ack messages when the user expands the new notification | ||||
|         let id = this._banner.connect('expanded', Lang.bind(this, this._ackMessages)); | ||||
|         this._banner.actor.connect('destroy', Lang.bind(this, | ||||
|             function() { | ||||
|                 this._banner.disconnect(id); | ||||
|                 this._banner = null; | ||||
|             })); | ||||
|  | ||||
|         return this._banner; | ||||
|     }, | ||||
|  | ||||
|     _updateAlias: function() { | ||||
|         let oldAlias = this.title; | ||||
|         let newAlias = this._contact.get_alias(); | ||||
| @@ -309,7 +330,8 @@ const ChatSource = new Lang.Class({ | ||||
|             return; | ||||
|  | ||||
|         this.setTitle(newAlias); | ||||
|         this._notification.appendAliasChange(oldAlias, newAlias); | ||||
|         if (this._notification) | ||||
|             this._notification.appendAliasChange(oldAlias, newAlias); | ||||
|     }, | ||||
|  | ||||
|     getIcon: function() { | ||||
| @@ -352,10 +374,16 @@ const ChatSource = new Lang.Class({ | ||||
|  | ||||
|     _updateAvatarIcon: function() { | ||||
|         this.iconUpdated(); | ||||
|         this._notification.update(this._notification.title, null, { customContent: true }); | ||||
|         if (this._notifiction) | ||||
|             this._notification.update(this._notification.title, | ||||
|                                       this._notification.bannerBodyText, | ||||
|                                       { gicon: this.getIcon() }); | ||||
|     }, | ||||
|  | ||||
|     open: function() { | ||||
|         Main.overview.hide(); | ||||
|         Main.panel.closeCalendar(); | ||||
|  | ||||
|         if (this._client.is_handling_channel(this._channel)) { | ||||
|             // We are handling the channel, try to pass it to Empathy or Polari | ||||
|             // (depending on the channel type) | ||||
| @@ -390,6 +418,7 @@ const ChatSource = new Lang.Class({ | ||||
|         let [success, events] = logManager.get_filtered_events_finish(result); | ||||
|  | ||||
|         let logMessages = events.map(makeMessageFromTplEvent); | ||||
|         this._ensureNotification(); | ||||
|  | ||||
|         let pendingTpMessages = this._channel.get_pending_messages(); | ||||
|         let pendingMessages = []; | ||||
| @@ -439,6 +468,18 @@ const ChatSource = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     destroy: function(reason) { | ||||
|         if (this._client.is_handling_channel(this._channel)) { | ||||
|             // The chat box has been destroyed so it can't | ||||
|             // handle the channel any more. | ||||
|             this._channel.close_async(function(channel, result) { | ||||
|                 channel.close_finish(result); | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         // Keep source alive while the channel is open | ||||
|         if (reason != MessageTray.NotificationDestroyedReason.SOURCE_CLOSED) | ||||
|             return; | ||||
|  | ||||
|         if (this._destroyed) | ||||
|             return; | ||||
|  | ||||
| @@ -452,9 +493,6 @@ const ChatSource = new Lang.Class({ | ||||
|         this._contact.disconnect(this._notifyAvatarId); | ||||
|         this._contact.disconnect(this._presenceChangedId); | ||||
|  | ||||
|         if (this._timestampTimeoutId) | ||||
|             Mainloop.source_remove(this._timestampTimeoutId); | ||||
|  | ||||
|         this.parent(reason); | ||||
|     }, | ||||
|  | ||||
| @@ -479,6 +517,7 @@ const ChatSource = new Lang.Class({ | ||||
|         if (message.get_message_type() == Tp.ChannelTextMessageType.DELIVERY_REPORT) | ||||
|             return; | ||||
|  | ||||
|         this._ensureNotification(); | ||||
|         this._pendingMessages.push(message); | ||||
|         this.countUpdated(); | ||||
|  | ||||
| @@ -506,6 +545,7 @@ const ChatSource = new Lang.Class({ | ||||
|     // This is called for both messages we send from | ||||
|     // our client and other clients as well. | ||||
|     _messageSent: function(channel, message, flags, token) { | ||||
|         this._ensureNotification(); | ||||
|         message = makeMessageFromTpMessage(message, NotificationDirection.SENT); | ||||
|         this._notification.appendMessage(message); | ||||
|     }, | ||||
| @@ -543,14 +583,10 @@ const ChatSource = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _presenceChanged: function (contact, presence, status, message) { | ||||
|         let msg, title; | ||||
|  | ||||
|         title = GLib.markup_escape_text(this.title, -1); | ||||
|  | ||||
|         this._notification.update(this._notification.title, null, { customContent: true, secondaryGIcon: this.getSecondaryIcon() }); | ||||
|  | ||||
|         if (message) | ||||
|             msg += ' <i>(' + GLib.markup_escape_text(message, -1) + ')</i>'; | ||||
|         if (this._notification) | ||||
|             this._notification.update(this._notification.title, | ||||
|                                       this._notification.bannerBodyText, | ||||
|                                       { secondaryGIcon: this.getSecondaryIcon() }); | ||||
|     }, | ||||
|  | ||||
|     _pendingRemoved: function(channel, message) { | ||||
| @@ -560,6 +596,10 @@ const ChatSource = new Lang.Class({ | ||||
|             this._pendingMessages.splice(idx, 1); | ||||
|             this.countUpdated(); | ||||
|         } | ||||
|  | ||||
|         if (this._pendingMessages.length == 0 && | ||||
|             this._banner && !this._banner.expanded) | ||||
|             this._banner.hide(); | ||||
|     }, | ||||
|  | ||||
|     _ackMessages: function() { | ||||
| @@ -574,42 +614,20 @@ const ChatNotification = new Lang.Class({ | ||||
|     Extends: MessageTray.Notification, | ||||
|  | ||||
|     _init: function(source) { | ||||
|         this.parent(source, source.title, null, { customContent: true, secondaryGIcon: source.getSecondaryIcon() }); | ||||
|         this.parent(source, source.title, null, | ||||
|                     { secondaryGIcon: source.getSecondaryIcon() }); | ||||
|         this.setUrgency(MessageTray.Urgency.HIGH); | ||||
|         this.setResident(true); | ||||
|  | ||||
|         this._responseEntry = new St.Entry({ style_class: 'chat-response', | ||||
|                                              can_focus: true }); | ||||
|         this._responseEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivated)); | ||||
|         this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged)); | ||||
|         this.setActionArea(this._responseEntry); | ||||
|  | ||||
|         this._responseEntry.clutter_text.connect('key-focus-in', Lang.bind(this, function() { | ||||
|             this.focused = true; | ||||
|         })); | ||||
|         this._responseEntry.clutter_text.connect('key-focus-out', Lang.bind(this, function() { | ||||
|             this.focused = false; | ||||
|             this.emit('unfocused'); | ||||
|         })); | ||||
|  | ||||
|         this._createScrollArea(); | ||||
|         this._lastGroup = null; | ||||
|  | ||||
|         // Keep track of the bottom position for the current adjustment and | ||||
|         // force a scroll to the bottom if things change while we were at the | ||||
|         // bottom | ||||
|         this._oldMaxScrollValue = this._scrollArea.vscroll.adjustment.value; | ||||
|         this._scrollArea.add_style_class_name('chat-notification-scrollview'); | ||||
|         this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) { | ||||
|             if (adjustment.value == this._oldMaxScrollValue) | ||||
|                 this.scrollTo(St.Side.BOTTOM); | ||||
|             this._oldMaxScrollValue = Math.max(adjustment.lower, adjustment.upper - adjustment.page_size); | ||||
|         })); | ||||
|  | ||||
|         this._inputHistory = new History.HistoryManager({ entry: this._responseEntry.clutter_text }); | ||||
|  | ||||
|         this._history = []; | ||||
|         this.messages = []; | ||||
|         this._timestampTimeoutId = 0; | ||||
|         this._composingTimeoutId = 0; | ||||
|     }, | ||||
|  | ||||
|     destroy: function(reason) { | ||||
|         if (this._timestampTimeoutId) | ||||
|             Mainloop.source_remove(this._timestampTimeoutId); | ||||
|         this._timestampTimeoutId = 0; | ||||
|         this.parent(reason); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
| @@ -635,10 +653,8 @@ const ChatNotification = new Lang.Class({ | ||||
|             styles.push('chat-action'); | ||||
|         } | ||||
|  | ||||
|         if (message.direction == NotificationDirection.RECEIVED) { | ||||
|             this.update(this.source.title, messageBody, { customContent: true, | ||||
|                                                           bannerMarkup: true }); | ||||
|         } | ||||
|         if (message.direction == NotificationDirection.RECEIVED) | ||||
|             this.update(this.source.title, messageBody, { bannerMarkup: true }); | ||||
|  | ||||
|         let group = (message.direction == NotificationDirection.RECEIVED ? | ||||
|                      'received' : 'sent'); | ||||
| @@ -651,10 +667,10 @@ const ChatNotification = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _filterMessages: function() { | ||||
|         if (this._history.length < 1) | ||||
|         if (this.messages.length < 1) | ||||
|             return; | ||||
|  | ||||
|         let lastMessageTime = this._history[0].time; | ||||
|         let lastMessageTime = this.messages[0].timestamp; | ||||
|         let currentTime = (Date.now() / 1000); | ||||
|  | ||||
|         // Keep the scrollback from growing too long. If the most | ||||
| @@ -666,12 +682,12 @@ const ChatNotification = new Lang.Class({ | ||||
|         let maxLength = (lastMessageTime < currentTime - SCROLLBACK_RECENT_TIME) ? | ||||
|             SCROLLBACK_IDLE_LENGTH : SCROLLBACK_RECENT_LENGTH; | ||||
|  | ||||
|         let filteredHistory = this._history.filter(function(item) { return item.realMessage }); | ||||
|         let filteredHistory = this.messages.filter(function(item) { return item.realMessage }); | ||||
|         if (filteredHistory.length > maxLength) { | ||||
|             let lastMessageToKeep = filteredHistory[maxLength]; | ||||
|             let expired = this._history.splice(this._history.indexOf(lastMessageToKeep)); | ||||
|             let expired = this.messages.splice(this.messages.indexOf(lastMessageToKeep)); | ||||
|             for (let i = 0; i < expired.length; i++) | ||||
|                 expired[i].actor.destroy(); | ||||
|                 this.emit('message-removed', expired[i]); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -684,7 +700,6 @@ const ChatNotification = new Lang.Class({ | ||||
|      *  styles: Style class names for the message to have. | ||||
|      *  timestamp: The timestamp of the message. | ||||
|      *  noTimestamp: suppress timestamp signal? | ||||
|      *  childProps: props to add the actor with. | ||||
|      */ | ||||
|     _append: function(props) { | ||||
|         let currentTime = (Date.now() / 1000); | ||||
| @@ -692,44 +707,23 @@ const ChatNotification = new Lang.Class({ | ||||
|                                       group: null, | ||||
|                                       styles: [], | ||||
|                                       timestamp: currentTime, | ||||
|                                       noTimestamp: false, | ||||
|                                       childProps: null }); | ||||
|                                       noTimestamp: false }); | ||||
|  | ||||
|         // Reset the old message timeout | ||||
|         if (this._timestampTimeoutId) | ||||
|             Mainloop.source_remove(this._timestampTimeoutId); | ||||
|         this._timestampTimeoutId = 0; | ||||
|  | ||||
|         let highlighter = new MessageTray.URLHighlighter(props.body, | ||||
|                                                          true,  // line wrap? | ||||
|                                                          true); // allow markup? | ||||
|         let message = { realMessage: props.group != 'meta', | ||||
|                         showTimestamp: false }; | ||||
|         Lang.copyProperties(props, message); | ||||
|         delete message.noTimestamp; | ||||
|  | ||||
|         let body = highlighter.actor; | ||||
|  | ||||
|         let styles = props.styles; | ||||
|         for (let i = 0; i < styles.length; i++) | ||||
|             body.add_style_class_name(styles[i]); | ||||
|  | ||||
|         let group = props.group; | ||||
|         if (group != this._lastGroup) { | ||||
|             this._lastGroup = group; | ||||
|             let emptyLine = new St.Label({ style_class: 'chat-empty-line' }); | ||||
|             this.addActor(emptyLine); | ||||
|             this._history.unshift({ actor: emptyLine, time: timestamp, | ||||
|                                     realMessage: false }); | ||||
|         } | ||||
|  | ||||
|         let lineBox = new St.BoxLayout({ vertical: false }); | ||||
|         lineBox.add(body, props.childProps); | ||||
|         this.addActor(lineBox); | ||||
|         this._lastMessageBox = lineBox; | ||||
|  | ||||
|         this.updated(); | ||||
|  | ||||
|         let timestamp = props.timestamp; | ||||
|         this._history.unshift({ actor: lineBox, time: timestamp, | ||||
|                                 realMessage: group != 'meta' }); | ||||
|         this.messages.unshift(message); | ||||
|         this.emit('message-added', message); | ||||
|  | ||||
|         if (!props.noTimestamp) { | ||||
|             let timestamp = props.timestamp; | ||||
|             if (timestamp < currentTime - SCROLLBACK_IMMEDIATE_TIME) { | ||||
|                 this.appendTimestamp(); | ||||
|             } else { | ||||
| @@ -748,15 +742,8 @@ const ChatNotification = new Lang.Class({ | ||||
|     appendTimestamp: function() { | ||||
|         this._timestampTimeoutId = 0; | ||||
|  | ||||
|         let lastMessageTime = this._history[0].time; | ||||
|         let lastMessageDate = new Date(lastMessageTime * 1000); | ||||
|  | ||||
|         let timeLabel = Util.createTimeLabel(lastMessageDate); | ||||
|         timeLabel.style_class = 'chat-meta-message'; | ||||
|         timeLabel.x_expand = timeLabel.y_expand = true; | ||||
|         timeLabel.x_align = timeLabel.y_align = Clutter.ActorAlign.END; | ||||
|  | ||||
|         this._lastMessageBox.add_actor(timeLabel); | ||||
|         this.messages[0].showTimestamp = true; | ||||
|         this.emit('timestamp-changed', this.messages[0]); | ||||
|  | ||||
|         this._filterMessages(); | ||||
|  | ||||
| @@ -771,13 +758,154 @@ const ChatNotification = new Lang.Class({ | ||||
|            IM name. */ | ||||
|         let message = '<i>' + _("%s is now known as %s").format(oldAlias, newAlias) + '</i>'; | ||||
|  | ||||
|         let label = this._append({ body: message, | ||||
|                                    group: 'meta', | ||||
|                                    styles: ['chat-meta-message'] }); | ||||
|  | ||||
|         this.update(newAlias, null, { customContent: true }); | ||||
|         this._append({ body: message, | ||||
|                        group: 'meta', | ||||
|                        styles: ['chat-meta-message'] }); | ||||
|  | ||||
|         this._filterMessages(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const ChatLineBox = new Lang.Class({ | ||||
|     Name: 'ChatLineBox', | ||||
|     Extends: St.BoxLayout, | ||||
|  | ||||
|     vfunc_get_preferred_height: function(forWidth) { | ||||
|         let [, natHeight] = this.parent(forWidth); | ||||
|         return [natHeight, natHeight]; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const ChatNotificationBanner = new Lang.Class({ | ||||
|     Name: 'ChatNotificationBanner', | ||||
|     Extends: MessageTray.NotificationBanner, | ||||
|  | ||||
|     _init: function(notification) { | ||||
|         this.parent(notification); | ||||
|  | ||||
|         this._responseEntry = new St.Entry({ style_class: 'chat-response', | ||||
|                                              x_expand: true, | ||||
|                                              can_focus: true }); | ||||
|         this._responseEntry.clutter_text.connect('activate', Lang.bind(this, this._onEntryActivated)); | ||||
|         this._responseEntry.clutter_text.connect('text-changed', Lang.bind(this, this._onEntryChanged)); | ||||
|         this.setActionArea(this._responseEntry); | ||||
|  | ||||
|         this._responseEntry.clutter_text.connect('key-focus-in', Lang.bind(this, function() { | ||||
|             this.focused = true; | ||||
|         })); | ||||
|         this._responseEntry.clutter_text.connect('key-focus-out', Lang.bind(this, function() { | ||||
|             this.focused = false; | ||||
|             this.emit('unfocused'); | ||||
|         })); | ||||
|  | ||||
|         this._scrollArea = new St.ScrollView({ style_class: 'chat-scrollview vfade', | ||||
|                                                vscrollbar_policy: Gtk.PolicyType.AUTOMATIC, | ||||
|                                                hscrollbar_policy: Gtk.PolicyType.NEVER, | ||||
|                                                visible: this.expanded }); | ||||
|         this._contentArea = new St.BoxLayout({ style_class: 'chat-body', | ||||
|                                                vertical: true }); | ||||
|         this._scrollArea.add_actor(this._contentArea); | ||||
|  | ||||
|         this.setExpandedBody(this._scrollArea); | ||||
|         this.setExpandedLines(CHAT_EXPAND_LINES); | ||||
|  | ||||
|         this._lastGroup = null; | ||||
|  | ||||
|         // Keep track of the bottom position for the current adjustment and | ||||
|         // force a scroll to the bottom if things change while we were at the | ||||
|         // bottom | ||||
|         this._oldMaxScrollValue = this._scrollArea.vscroll.adjustment.value; | ||||
|         this._scrollArea.vscroll.adjustment.connect('changed', Lang.bind(this, function(adjustment) { | ||||
|             if (adjustment.value == this._oldMaxScrollValue) | ||||
|                 this.scrollTo(St.Side.BOTTOM); | ||||
|             this._oldMaxScrollValue = Math.max(adjustment.lower, adjustment.upper - adjustment.page_size); | ||||
|         })); | ||||
|  | ||||
|         this._inputHistory = new History.HistoryManager({ entry: this._responseEntry.clutter_text }); | ||||
|  | ||||
|         this._composingTimeoutId = 0; | ||||
|  | ||||
|         this._messageActors = new Map(); | ||||
|  | ||||
|         this._messageAddedId = this.notification.connect('message-added', | ||||
|             Lang.bind(this, function(n, message) { | ||||
|                 this._addMessage(message); | ||||
|             })); | ||||
|         this._messageRemovedId = this.notification.connect('message-removed', | ||||
|             Lang.bind(this, function(n, message) { | ||||
|                 let actor = this._messageActors.get(message); | ||||
|                 if (this._messageActors.delete(message)) | ||||
|                     actor.destroy(); | ||||
|             })); | ||||
|         this._timestampChangedId = this.notification.connect('timestamp-changed', | ||||
|             Lang.bind(this, function(n, message) { | ||||
|                 this._updateTimestamp(message); | ||||
|             })); | ||||
|  | ||||
|         for (let i = this.notification.messages.length - 1; i >= 0; i--) | ||||
|             this._addMessage(this.notification.messages[i]); | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
|         this.parent(); | ||||
|         this.notification.disconnect(this._messageAddedId); | ||||
|         this.notification.disconnect(this._messageRemovedId); | ||||
|         this.notification.disconnect(this._timestampChangedId); | ||||
|     }, | ||||
|  | ||||
|     scrollTo: function(side) { | ||||
|         let adjustment = this._scrollArea.vscroll.adjustment; | ||||
|         if (side == St.Side.TOP) | ||||
|             adjustment.value = adjustment.lower; | ||||
|         else if (side == St.Side.BOTTOM) | ||||
|             adjustment.value = adjustment.upper; | ||||
|     }, | ||||
|  | ||||
|     hide: function() { | ||||
|         this.emit('done-displaying'); | ||||
|     }, | ||||
|  | ||||
|     _addMessage: function(message) { | ||||
|         let highlighter = new Calendar.URLHighlighter(message.body, true, true); | ||||
|         let body = highlighter.actor; | ||||
|  | ||||
|         let styles = message.styles; | ||||
|         for (let i = 0; i < styles.length; i++) | ||||
|             body.add_style_class_name(styles[i]); | ||||
|  | ||||
|         let group = message.group; | ||||
|         if (group != this._lastGroup) { | ||||
|             this._lastGroup = group; | ||||
|             body.add_style_class_name('chat-new-group'); | ||||
|         } | ||||
|  | ||||
|         let lineBox = new ChatLineBox(); | ||||
|         lineBox.add(body); | ||||
|         this._contentArea.add_actor(lineBox); | ||||
|         this._messageActors.set(message, lineBox); | ||||
|  | ||||
|         this._updateTimestamp(message); | ||||
|     }, | ||||
|  | ||||
|     _updateTimestamp: function(message) { | ||||
|         let actor = this._messageActors.get(message); | ||||
|         if (!actor) | ||||
|             return; | ||||
|  | ||||
|         while (actor.get_n_children() > 1) | ||||
|             actor.get_child_at_index(1).destroy(); | ||||
|  | ||||
|         if (message.showTimestamp) { | ||||
|             let lastMessageTime = message.timestamp; | ||||
|             let lastMessageDate = new Date(lastMessageTime * 1000); | ||||
|  | ||||
|             let timeLabel = Util.createTimeLabel(lastMessageDate); | ||||
|             timeLabel.style_class = 'chat-meta-message'; | ||||
|             timeLabel.x_expand = timeLabel.y_expand = true; | ||||
|             timeLabel.x_align = timeLabel.y_align = Clutter.ActorAlign.END; | ||||
|  | ||||
|             actor.add_actor(timeLabel); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onEntryActivated: function() { | ||||
| @@ -790,13 +918,13 @@ const ChatNotification = new Lang.Class({ | ||||
|         // Telepathy sends out the Sent signal for us. | ||||
|         // see Source._messageSent | ||||
|         this._responseEntry.set_text(''); | ||||
|         this.source.respond(text); | ||||
|         this.notification.source.respond(text); | ||||
|     }, | ||||
|  | ||||
|     _composingStopTimeout: function() { | ||||
|         this._composingTimeoutId = 0; | ||||
|  | ||||
|         this.source.setChatState(Tp.ChannelChatState.PAUSED); | ||||
|         this.notification.source.setChatState(Tp.ChannelChatState.PAUSED); | ||||
|  | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
| @@ -816,14 +944,14 @@ const ChatNotification = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         if (text != '') { | ||||
|             this.source.setChatState(Tp.ChannelChatState.COMPOSING); | ||||
|             this.notification.source.setChatState(Tp.ChannelChatState.COMPOSING); | ||||
|  | ||||
|             this._composingTimeoutId = Mainloop.timeout_add_seconds( | ||||
|                 COMPOSING_STOP_TIMEOUT, | ||||
|                 Lang.bind(this, this._composingStopTimeout)); | ||||
|             GLib.Source.set_name_by_id(this._composingTimeoutId, '[gnome-shell] this._composingStopTimeout'); | ||||
|         } else { | ||||
|             this.source.setChatState(Tp.ChannelChatState.ACTIVE); | ||||
|             this.notification.source.setChatState(Tp.ChannelChatState.ACTIVE); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -27,8 +27,8 @@ const CtrlAltTabManager = new Lang.Class({ | ||||
|     _init: function() { | ||||
|         this._items = []; | ||||
|         this.addGroup(global.window_group, _("Windows"), | ||||
|                       'emblem-documents-symbolic', { sortGroup: SortGroup.TOP, | ||||
|                                                      focusCallback: Lang.bind(this, this._focusWindows) }); | ||||
|                       'focus-windows-symbolic', { sortGroup: SortGroup.TOP, | ||||
|                                                   focusCallback: Lang.bind(this, this._focusWindows) }); | ||||
|     }, | ||||
|  | ||||
|     addGroup: function(root, name, icon, params) { | ||||
|   | ||||
| @@ -270,7 +270,7 @@ const ShowAppsIcon = new Lang.Class({ | ||||
|         if (app == null) | ||||
|             return false; | ||||
|  | ||||
|         if (!this._settings.is_writable('favorite-apps')) | ||||
|         if (!global.settings.is_writable('favorite-apps')) | ||||
|             return false; | ||||
|  | ||||
|         let id = app.get_id(); | ||||
| @@ -428,8 +428,6 @@ const Dash = new Lang.Class({ | ||||
|  | ||||
|         this._workId = Main.initializeDeferredWork(this._box, Lang.bind(this, this._redisplay)); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema_id: 'org.gnome.shell' }); | ||||
|  | ||||
|         this._appSystem = Shell.AppSystem.get_default(); | ||||
|  | ||||
|         this._appSystem.connect('installed-changed', Lang.bind(this, function() { | ||||
| @@ -646,15 +644,14 @@ const Dash = new Lang.Class({ | ||||
|         let firstIcon = firstButton._delegate.icon; | ||||
|  | ||||
|         let minHeight, natHeight; | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|  | ||||
|         // Enforce the current icon size during the size request | ||||
|         firstIcon.setIconSize(this.iconSize); | ||||
|         firstIcon.icon.ensure_style(); | ||||
|         let [currentWidth, currentHeight] = firstIcon.icon.get_size(); | ||||
|         firstIcon.icon.set_size(this.iconSize * scaleFactor, this.iconSize * scaleFactor); | ||||
|         [minHeight, natHeight] = firstButton.get_preferred_height(-1); | ||||
|  | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|         let iconSizes = baseIconSizes.map(function(s) { | ||||
|             return s * scaleFactor; | ||||
|         }); | ||||
|         firstIcon.icon.set_size(currentWidth, currentHeight); | ||||
|  | ||||
|         // Subtract icon padding and box spacing from the available height | ||||
|         availHeight -= iconChildren.length * (natHeight - this.iconSize * scaleFactor) + | ||||
| @@ -662,6 +659,10 @@ const Dash = new Lang.Class({ | ||||
|  | ||||
|         let availSize = availHeight / iconChildren.length; | ||||
|  | ||||
|         let iconSizes = baseIconSizes.map(function(s) { | ||||
|             return s * scaleFactor; | ||||
|         }); | ||||
|  | ||||
|         let newIconSize = baseIconSizes[0]; | ||||
|         for (let i = 0; i < iconSizes.length; i++) { | ||||
|             if (iconSizes[i] < availSize) | ||||
| @@ -862,7 +863,7 @@ const Dash = new Lang.Class({ | ||||
|         if (app == null || app.is_window_backed()) | ||||
|             return DND.DragMotionResult.NO_DROP; | ||||
|  | ||||
|         if (!this._settings.is_writable('favorite-apps')) | ||||
|         if (!global.settings.is_writable('favorite-apps')) | ||||
|             return DND.DragMotionResult.NO_DROP; | ||||
|  | ||||
|         let favorites = AppFavorites.getAppFavorites().getFavorites(); | ||||
| @@ -941,7 +942,7 @@ const Dash = new Lang.Class({ | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         if (!this._settings.is_writable('favorite-apps')) | ||||
|         if (!global.settings.is_writable('favorite-apps')) | ||||
|             return false; | ||||
|  | ||||
|         let id = app.get_id(); | ||||
|   | ||||
| @@ -357,8 +357,6 @@ const DateMenuButton = new Lang.Class({ | ||||
|                 this._date.setDate(now); | ||||
|                 this._messageList.setDate(now); | ||||
|             } | ||||
|             // Block notification banners while the menu is open | ||||
|             Main.messageTray.bannerBlocked = isOpen; | ||||
|         })); | ||||
|  | ||||
|         // Fill up the first column | ||||
|   | ||||
							
								
								
									
										71
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							
							
						
						| @@ -79,9 +79,12 @@ const _Draggable = new Lang.Class({ | ||||
|                                         dragActorOpacity: undefined }); | ||||
|  | ||||
|         this.actor = actor; | ||||
|         if (!params.manualMode) | ||||
|         if (!params.manualMode) { | ||||
|             this.actor.connect('button-press-event', | ||||
|                                Lang.bind(this, this._onButtonPress)); | ||||
|             this.actor.connect('touch-event', | ||||
|                                Lang.bind(this, this._onTouchEvent)); | ||||
|         } | ||||
|  | ||||
|         this.actor.connect('destroy', Lang.bind(this, function() { | ||||
|             this._actorDestroyed = true; | ||||
| @@ -121,8 +124,50 @@ const _Draggable = new Lang.Class({ | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onTouchEvent: function (actor, event) { | ||||
|         if (event.type() != Clutter.EventType.TOUCH_BEGIN || | ||||
|             !global.display.is_pointer_emulating_sequence(event.get_event_sequence())) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (Tweener.getTweenCount(actor)) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         this._touchSequence = event.get_event_sequence(); | ||||
|  | ||||
|         this._buttonDown = true; | ||||
|         this._grabActor(); | ||||
|  | ||||
|         let [stageX, stageY] = event.get_coords(); | ||||
|         this._dragStartX = stageX; | ||||
|         this._dragStartY = stageY; | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _grabDevice: function(actor) { | ||||
|         let manager = Clutter.DeviceManager.get_default(); | ||||
|         let pointer = manager.get_core_device(Clutter.InputDeviceType.POINTER_DEVICE); | ||||
|  | ||||
|         if (pointer && this._touchSequence) | ||||
|             pointer.sequence_grab(this._touchSequence, actor); | ||||
|         else if (pointer) | ||||
|             pointer.grab (actor); | ||||
|  | ||||
|         this._grabbedDevice = pointer; | ||||
|     }, | ||||
|  | ||||
|     _ungrabDevice: function() { | ||||
|         if (this._touchSequence) | ||||
|             this._grabbedDevice.sequence_ungrab (this._touchSequence); | ||||
|         else | ||||
|             this._grabbedDevice.ungrab(); | ||||
|  | ||||
|         this._touchSequence = null; | ||||
|         this._grabbedDevice = null; | ||||
|     }, | ||||
|  | ||||
|     _grabActor: function() { | ||||
|         Clutter.grab_pointer(this.actor); | ||||
|         this._grabDevice(this.actor); | ||||
|         this._onEventId = this.actor.connect('event', | ||||
|                                              Lang.bind(this, this._onEvent)); | ||||
|     }, | ||||
| @@ -131,7 +176,7 @@ const _Draggable = new Lang.Class({ | ||||
|         if (!this._onEventId) | ||||
|             return; | ||||
|  | ||||
|         Clutter.ungrab_pointer(); | ||||
|         this._ungrabDevice(); | ||||
|         this.actor.disconnect(this._onEventId); | ||||
|         this._onEventId = null; | ||||
|     }, | ||||
| @@ -140,13 +185,13 @@ const _Draggable = new Lang.Class({ | ||||
|         if (!this._eventsGrabbed) { | ||||
|             this._eventsGrabbed = Main.pushModal(_getEventHandlerActor()); | ||||
|             if (this._eventsGrabbed) | ||||
|                 Clutter.grab_pointer(_getEventHandlerActor()); | ||||
|                 this._grabDevice(_getEventHandlerActor()); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _ungrabEvents: function() { | ||||
|         if (this._eventsGrabbed) { | ||||
|             Clutter.ungrab_pointer(); | ||||
|             this._ungrabDevice(); | ||||
|             Main.popModal(_getEventHandlerActor()); | ||||
|             this._eventsGrabbed = false; | ||||
|         } | ||||
| @@ -157,7 +202,9 @@ const _Draggable = new Lang.Class({ | ||||
|         // didn't start the drag, to drop the draggable in case the drag was in progress, and | ||||
|         // to complete the drag and ensure that whatever happens to be under the pointer does | ||||
|         // not get triggered if the drag was cancelled with Esc. | ||||
|         if (event.type() == Clutter.EventType.BUTTON_RELEASE) { | ||||
|         if (event.type() == Clutter.EventType.BUTTON_RELEASE || | ||||
|             (event.type() == Clutter.EventType.TOUCH_END && | ||||
|              global.display.is_pointer_emulating_sequence(event.get_event_sequence()))) { | ||||
|             this._buttonDown = false; | ||||
|             if (this._dragInProgress) { | ||||
|                 return this._dragActorDropped(event); | ||||
| @@ -172,7 +219,9 @@ const _Draggable = new Lang.Class({ | ||||
|             } | ||||
|         // We intercept MOTION event to figure out if the drag has started and to draw | ||||
|         // this._dragActor under the pointer when dragging is in progress | ||||
|         } else if (event.type() == Clutter.EventType.MOTION) { | ||||
|         } else if (event.type() == Clutter.EventType.MOTION || | ||||
|                    (event.type() == Clutter.EventType.TOUCH_UPDATE && | ||||
|                     global.display.is_pointer_emulating_sequence(event.get_event_sequence()))) { | ||||
|             if (this._dragInProgress) { | ||||
|                 return this._updateDragPosition(event); | ||||
|             } else if (this._dragActor == null) { | ||||
| @@ -214,7 +263,7 @@ const _Draggable = new Lang.Class({ | ||||
|      * This function is useful to call if you've specified manualMode | ||||
|      * for the draggable. | ||||
|      */ | ||||
|     startDrag: function (stageX, stageY, time) { | ||||
|     startDrag: function (stageX, stageY, time, sequence) { | ||||
|         currentDraggable = this; | ||||
|         this._dragInProgress = true; | ||||
|  | ||||
| @@ -228,6 +277,8 @@ const _Draggable = new Lang.Class({ | ||||
|         this.emit('drag-begin', time); | ||||
|         if (this._onEventId) | ||||
|             this._ungrabActor(); | ||||
|  | ||||
|         this._touchSequence = sequence; | ||||
|         this._grabEvents(); | ||||
|         global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG); | ||||
|  | ||||
| @@ -338,8 +389,8 @@ const _Draggable = new Lang.Class({ | ||||
|         let threshold = Gtk.Settings.get_default().gtk_dnd_drag_threshold; | ||||
|         if ((Math.abs(stageX - this._dragStartX) > threshold || | ||||
|              Math.abs(stageY - this._dragStartY) > threshold)) { | ||||
|                 this.startDrag(stageX, stageY, event.get_time()); | ||||
|                 this._updateDragPosition(event); | ||||
|             this.startDrag(stageX, stageY, event.get_time(), this._touchSequence); | ||||
|             this._updateDragPosition(event); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|   | ||||
| @@ -81,7 +81,6 @@ function init() { | ||||
|  | ||||
|     // Miscellaneous monkeypatching | ||||
|     _patchContainerClass(St.BoxLayout); | ||||
|     _patchContainerClass(St.Table); | ||||
|  | ||||
|     _patchLayoutClass(Clutter.TableLayout, { row_spacing: 'spacing-rows', | ||||
|                                              column_spacing: 'spacing-columns' }); | ||||
|   | ||||
| @@ -211,7 +211,7 @@ const InstallExtensionDialog = new Lang.Class({ | ||||
|         let icon = new St.Icon({ gicon: gicon }); | ||||
|         box.add(icon); | ||||
|  | ||||
|         let label = new St.Label({ style_class: 'prompt-dialog-headline', | ||||
|         let label = new St.Label({ style_class: 'prompt-dialog-headline headline', | ||||
|                                    text: message }); | ||||
|         box.add(label); | ||||
|     }, | ||||
|   | ||||
| @@ -334,7 +334,7 @@ function _sessionUpdated() { | ||||
|     // from allowExtensions in the future | ||||
|     if (Main.sessionMode.allowExtensions) { | ||||
|         if (initted) | ||||
|             onEnabledExtensionsChanged(); | ||||
|             enabledExtensions = getEnabledExtensions(); | ||||
|         enableAllExtensions(); | ||||
|     } else { | ||||
|         disableAllExtensions(); | ||||
|   | ||||
| @@ -6,6 +6,7 @@ const Gdk = imports.gi.Gdk; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Signals = imports.signals; | ||||
| const St = imports.gi.St; | ||||
| @@ -23,9 +24,6 @@ const KEYBOARD_TYPE = 'keyboard-type'; | ||||
| const A11Y_APPLICATIONS_SCHEMA = 'org.gnome.desktop.a11y.applications'; | ||||
| const SHOW_KEYBOARD = 'screen-keyboard-enabled'; | ||||
|  | ||||
| const CURSOR_BUS_NAME = 'org.gnome.SettingsDaemon.Cursor'; | ||||
| const CURSOR_OBJECT_PATH = '/org/gnome/SettingsDaemon/Cursor'; | ||||
|  | ||||
| const CARIBOU_BUS_NAME = 'org.gnome.Caribou.Daemon'; | ||||
| const CARIBOU_OBJECT_PATH = '/org/gnome/Caribou/Daemon'; | ||||
|  | ||||
| @@ -60,14 +58,7 @@ const CaribouDaemonIface = '<node> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const CursorManagerIface = '<node> \ | ||||
| <interface name="org.gnome.SettingsDaemon.Cursor"> \ | ||||
| <property name="ShowOSK" type="b" access="read" /> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const CaribouDaemonProxy = Gio.DBusProxy.makeProxyWrapper(CaribouDaemonIface); | ||||
| const CursorManagerProxy = Gio.DBusProxy.makeProxyWrapper(CursorManagerIface); | ||||
|  | ||||
| const Key = new Lang.Class({ | ||||
|     Name: 'Key', | ||||
| @@ -120,6 +111,35 @@ const Key = new Lang.Class({ | ||||
|                 key.release(); | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|             })); | ||||
|         button.connect('touch-event', Lang.bind(this, | ||||
|             function (actor, event) { | ||||
|                 let device = event.get_device(); | ||||
|                 let sequence = event.get_event_sequence(); | ||||
|  | ||||
|                 // We only handle touch events here on wayland. On X11 | ||||
|                 // we do get emulated pointer events, which already works | ||||
|                 // for single-touch cases. Besides, the X11 passive touch grab | ||||
|                 // set up by Mutter will make us see first the touch events | ||||
|                 // and later the pointer events, so it will look like two | ||||
|                 // unrelated series of events, we want to avoid double handling | ||||
|                 // in these cases. | ||||
|                 if (!Meta.is_wayland_compositor()) | ||||
|                     return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|                 if (!this._touchPressed && | ||||
|                     event.type() == Clutter.EventType.TOUCH_BEGIN) { | ||||
|                     device.sequence_grab(sequence, actor); | ||||
|                     this._touchPressed = true; | ||||
|                     key.press(); | ||||
|                 } else if (this._touchPressed && | ||||
|                            event.type() == Clutter.EventType.TOUCH_END && | ||||
|                            device.sequence_get_grabbed_actor(sequence) == actor) { | ||||
|                     device.sequence_ungrab(sequence); | ||||
|                     this._touchPressed = false; | ||||
|                     key.release(); | ||||
|                 } | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|             })); | ||||
|  | ||||
|         return button; | ||||
|     }, | ||||
| @@ -184,21 +204,23 @@ const Keyboard = new Lang.Class({ | ||||
|         this._keyboardSettings.connect('changed', Lang.bind(this, this._sync)); | ||||
|         this._a11yApplicationsSettings = new Gio.Settings({ schema_id: A11Y_APPLICATIONS_SCHEMA }); | ||||
|         this._a11yApplicationsSettings.connect('changed', Lang.bind(this, this._sync)); | ||||
|         this._watchNameId = Gio.bus_watch_name(Gio.BusType.SESSION, CURSOR_BUS_NAME, 0, | ||||
|                                                Lang.bind(this, this._sync), | ||||
|                                                Lang.bind(this, this._sync)); | ||||
|         this._daemonProxy = null; | ||||
|         this._cursorProxy = new CursorManagerProxy(Gio.DBus.session, CURSOR_BUS_NAME, | ||||
|                                                    CURSOR_OBJECT_PATH, | ||||
|                                                    Lang.bind(this, function(proxy, error) { | ||||
|                                                        if (error) { | ||||
|                                                            log(error.message); | ||||
|                                                            return; | ||||
|                                                        } | ||||
|                                                        this._cursorProxy.connect('g-properties-changed', | ||||
|                                                                                  Lang.bind(this, this._sync)); | ||||
|                                                        this._sync(); | ||||
|                                                    })); | ||||
|         this._lastDeviceId = null; | ||||
|  | ||||
|         if (Meta.is_wayland_compositor() && | ||||
|             Caribou.DisplayAdapter.set_default) | ||||
|             Caribou.DisplayAdapter.set_default(new ShellWaylandAdapter()); | ||||
|  | ||||
|         Meta.get_backend().connect('last-device-changed', Lang.bind(this, | ||||
|             function (backend, deviceId) { | ||||
|                 let manager = Clutter.DeviceManager.get_default(); | ||||
|                 let device = manager.get_device(deviceId); | ||||
|  | ||||
|                 if (device.get_device_name().indexOf('XTEST') < 0) { | ||||
|                     this._lastDeviceId = deviceId; | ||||
|                     this._sync(); | ||||
|                 } | ||||
|             })); | ||||
|         this._sync(); | ||||
|  | ||||
|         this._showIdleId = 0; | ||||
| @@ -217,9 +239,22 @@ const Keyboard = new Lang.Class({ | ||||
|         this._redraw(); | ||||
|     }, | ||||
|  | ||||
|     _lastDeviceIsTouchscreen: function () { | ||||
|         if (!this._lastDeviceId) | ||||
|             return false; | ||||
|  | ||||
|         let manager = Clutter.DeviceManager.get_default(); | ||||
|         let device = manager.get_device(this._lastDeviceId); | ||||
|  | ||||
|         if (!device) | ||||
|             return false; | ||||
|  | ||||
|         return device.get_device_type() == Clutter.InputDeviceType.TOUCHSCREEN_DEVICE; | ||||
|     }, | ||||
|  | ||||
|     _sync: function () { | ||||
|         this._enableKeyboard = this._a11yApplicationsSettings.get_boolean(SHOW_KEYBOARD) || | ||||
|                                this._cursorProxy.ShowOSK; | ||||
|                                this._lastDeviceIsTouchscreen(); | ||||
|         if (!this._enableKeyboard && !this._keyboard) | ||||
|             return; | ||||
|         if (this._enableKeyboard && this._keyboard && | ||||
| @@ -717,3 +752,24 @@ const KeyboardSource = new Lang.Class({ | ||||
|         this._keyboard.show(Main.layoutManager.bottomIndex); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const ShellWaylandAdapter = new Lang.Class({ | ||||
|     Name: 'ShellWaylandAdapter', | ||||
|     Extends: Caribou.XAdapter, | ||||
|  | ||||
|     vfunc_keyval_press: function(keyval) { | ||||
|         let focus = global.stage.get_key_focus(); | ||||
|         if (focus instanceof Clutter.Text) | ||||
|             Shell.util_text_insert_keyval(focus, keyval); | ||||
|         else | ||||
|             this.parent(keyval); | ||||
|     }, | ||||
|  | ||||
|     vfunc_keyval_release: function(keyval) { | ||||
|         let focus = global.stage.get_key_focus(); | ||||
|         if (focus instanceof Clutter.Text) | ||||
|             return;             // do nothing | ||||
|         else | ||||
|             this.parent(keyval); | ||||
|     }, | ||||
| }); | ||||
|   | ||||
| @@ -11,6 +11,7 @@ const St = imports.gi.St; | ||||
|  | ||||
| const Background = imports.ui.background; | ||||
| const BackgroundMenu = imports.ui.backgroundMenu; | ||||
| const LoginManager = imports.misc.loginManager; | ||||
|  | ||||
| const DND = imports.ui.dnd; | ||||
| const Main = imports.ui.main; | ||||
| @@ -273,6 +274,18 @@ const LayoutManager = new Lang.Class({ | ||||
|         global.screen.connect('in-fullscreen-changed', | ||||
|                               Lang.bind(this, this._updateFullscreen)); | ||||
|         this._monitorsChanged(); | ||||
|  | ||||
|         // NVIDIA drivers don't preserve FBO contents across | ||||
|         // suspend/resume, see | ||||
|         // https://bugzilla.gnome.org/show_bug.cgi?id=739178 | ||||
|         if (Shell.util_need_background_refresh()) { | ||||
|             LoginManager.getLoginManager().connect('prepare-for-sleep', | ||||
|                                                    function(lm, suspending) { | ||||
|                                                        if (suspending) | ||||
|                                                            return; | ||||
|                                                        Meta.Background.refresh_all(); | ||||
|                                                    }); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     // This is called by Main after everything else is constructed | ||||
| @@ -824,6 +837,7 @@ const LayoutManager = new Lang.Class({ | ||||
|         // need to connect to 'destroy' too. | ||||
|  | ||||
|         this._trackedActors.push(actorData); | ||||
|         this._updateActorVisibility(actorData); | ||||
|         this._queueUpdateRegions(); | ||||
|     }, | ||||
|  | ||||
| @@ -842,25 +856,23 @@ const LayoutManager = new Lang.Class({ | ||||
|         this._queueUpdateRegions(); | ||||
|     }, | ||||
|  | ||||
|     _updateActorVisibility: function(actorData) { | ||||
|         if (!actorData.trackFullscreen) | ||||
|             return; | ||||
|  | ||||
|         let monitor = this.findMonitorForActor(actorData.actor); | ||||
|         actorData.actor.visible = !(global.window_group.visible && | ||||
|                                     monitor && | ||||
|                                     monitor.inFullscreen); | ||||
|     }, | ||||
|  | ||||
|     _updateVisibility: function() { | ||||
|         let windowsVisible = Main.sessionMode.hasWindows && !this._inOverview; | ||||
|  | ||||
|         global.window_group.visible = windowsVisible; | ||||
|         global.top_window_group.visible = windowsVisible; | ||||
|  | ||||
|         for (let i = 0; i < this._trackedActors.length; i++) { | ||||
|             let actorData = this._trackedActors[i], visible; | ||||
|             if (!actorData.trackFullscreen) | ||||
|                 continue; | ||||
|  | ||||
|             if (!windowsVisible) | ||||
|                 visible = true; | ||||
|             else if (this.findMonitorForActor(actorData.actor).inFullscreen) | ||||
|                 visible = false; | ||||
|             else | ||||
|                 visible = true; | ||||
|             actorData.actor.visible = visible; | ||||
|         } | ||||
|         this._trackedActors.forEach(Lang.bind(this, this._updateActorVisibility)); | ||||
|     }, | ||||
|  | ||||
|     getWorkAreaForMonitor: function(monitorIndex) { | ||||
| @@ -1303,8 +1315,11 @@ const PressureBarrier = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onBarrierLeft: function(barrier, event) { | ||||
|         this._reset(); | ||||
|         this._isTriggered = false; | ||||
|         barrier._isHit = false; | ||||
|         if (this._barriers.every(function(b) { return !b._isHit; })) { | ||||
|             this._reset(); | ||||
|             this._isTriggered = false; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _trigger: function() { | ||||
| @@ -1314,6 +1329,8 @@ const PressureBarrier = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onBarrierHit: function(barrier, event) { | ||||
|         barrier._isHit = true; | ||||
|  | ||||
|         // If we've triggered the barrier, wait until the pointer has the | ||||
|         // left the barrier hitbox until we trigger it again. | ||||
|         if (this._isTriggered) | ||||
|   | ||||
| @@ -1,8 +1,11 @@ | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GLib = imports.gi.GLib; | ||||
| const GObject = imports.gi.GObject; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const CtrlAltTab = imports.ui.ctrlAltTab; | ||||
| const Lang = imports.lang; | ||||
| const Layout = imports.ui.layout; | ||||
| const Main = imports.ui.main; | ||||
| @@ -26,8 +29,12 @@ const STANDARD_TRAY_ICON_IMPLEMENTATIONS = { | ||||
| }; | ||||
|  | ||||
| // Offset of the original position from the bottom-right corner | ||||
| const CONCEALED_VISIBLE_FRACTION = 0.2; | ||||
| const CONCEALED_WIDTH = 3; | ||||
| const REVEAL_ANIMATION_TIME = 0.2; | ||||
| const TEMP_REVEAL_TIME = 2; | ||||
|  | ||||
| const BARRIER_THRESHOLD = 70; | ||||
| const BARRIER_TIMEOUT = 1000; | ||||
|  | ||||
| const LegacyTray = new Lang.Class({ | ||||
|     Name: 'LegacyTray', | ||||
| @@ -49,11 +56,15 @@ const LegacyTray = new Lang.Class({ | ||||
|                                        y_align: Clutter.ActorAlign.END, | ||||
|                                        layout_manager: this._slideLayout }); | ||||
|         this.actor.add_actor(this._slider); | ||||
|         this._slider.connect('notify::allocation', Lang.bind(this, this._syncBarrier)); | ||||
|  | ||||
|         this._box = new St.BoxLayout(); | ||||
|         this._slider.add_actor(this._box); | ||||
|  | ||||
|         this._concealHandle = new St.Button({ style_class: 'legacy-tray-handle' }); | ||||
|         this._concealHandle = new St.Button({ style_class: 'legacy-tray-handle', | ||||
|                                               /* translators: 'Hide' is a verb */ | ||||
|                                               accessible_name: _("Hide tray"), | ||||
|                                               can_focus: true }); | ||||
|         this._concealHandle.child = new St.Icon({ icon_name: 'go-previous-symbolic' }); | ||||
|         this._box.add_child(this._concealHandle); | ||||
|  | ||||
| @@ -81,12 +92,24 @@ const LegacyTray = new Lang.Class({ | ||||
|                 this._revealHandle.show(); | ||||
|             })); | ||||
|  | ||||
|         this._horizontalBarrier = null; | ||||
|         this._pressureBarrier = new Layout.PressureBarrier(BARRIER_THRESHOLD, | ||||
|                                                            BARRIER_TIMEOUT, | ||||
|                                                            Shell.ActionMode.NORMAL); | ||||
|         this._pressureBarrier.connect('trigger', Lang.bind(this, function() { | ||||
|             this._concealHandle.show(); | ||||
|         })); | ||||
|  | ||||
|         Main.layoutManager.addChrome(this.actor, { affectsInputRegion: false }); | ||||
|         Main.layoutManager.trackChrome(this._slider, { affectsInputRegion: true }); | ||||
|         Main.uiGroup.set_child_below_sibling(this.actor, Main.layoutManager.modalDialogGroup); | ||||
|         Main.ctrlAltTabManager.addGroup(this.actor, | ||||
|                                         _("Status Icons"), 'focus-legacy-systray-symbolic', | ||||
|                                         { sortGroup: CtrlAltTab.SortGroup.BOTTOM }); | ||||
|  | ||||
|         this._trayManager = new Shell.TrayManager(); | ||||
|         this._trayManager.connect('tray-icon-added', Lang.bind(this, this._onTrayIconAdded)); | ||||
|         this._trayManager.connect('tray-icon-removed', Lang.bind(this, this._onTrayIconRemoved)); | ||||
|         this._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)); | ||||
|         this._trayManager.manage_screen(global.screen, this.actor); | ||||
|  | ||||
|         Main.overview.connect('showing', Lang.bind(this, | ||||
| @@ -121,17 +144,47 @@ const LegacyTray = new Lang.Class({ | ||||
|             return; | ||||
|  | ||||
|         let button = new St.Button({ child: icon, | ||||
|                                      style_class: 'legacy-tray-icon', | ||||
|                                      button_mask: St.ButtonMask.ONE | | ||||
|                                                   St.ButtonMask.TWO | | ||||
|                                                   St.ButtonMask.THREE, | ||||
|                                      can_focus: true, | ||||
|                                      x_fill: true, y_fill: true }); | ||||
|  | ||||
|         let app = Shell.WindowTracker.get_default().get_app_from_pid(icon.pid); | ||||
|         if (!app) | ||||
|             app = Shell.AppSystem.get_default().lookup_startup_wmclass(wmClass); | ||||
|         if (!app) | ||||
|             app = Shell.AppSystem.get_default().lookup_desktop_wmclass(wmClass); | ||||
|         if (app) | ||||
|             button.accessible_name = app.get_name(); | ||||
|         else | ||||
|             button.accessible_name = icon.title; | ||||
|  | ||||
|         button.connect('clicked', | ||||
|             function() { | ||||
|                 icon.click(Clutter.get_current_event()); | ||||
|             }); | ||||
|         button.connect('key-press-event', | ||||
|             function() { | ||||
|                 icon.click(Clutter.get_current_event()); | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|             }); | ||||
|         button.connect('key-focus-in', Lang.bind(this, | ||||
|             function() { | ||||
|                 this._concealHandle.show(); | ||||
|             })); | ||||
|  | ||||
|         this._iconBox.add_actor(button); | ||||
|         this._sync(); | ||||
|  | ||||
|         if (!this._concealHandle.visible) { | ||||
|             this._concealHandle.show(); | ||||
|             GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, TEMP_REVEAL_TIME, | ||||
|                 Lang.bind(this, function() { | ||||
|                     this._concealHandle.hide(); | ||||
|                     return GLib.SOURCE_REMOVE; | ||||
|                 })); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onTrayIconRemoved: function(tm, icon) { | ||||
| @@ -142,6 +195,45 @@ const LegacyTray = new Lang.Class({ | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     _syncBarrier: function() { | ||||
|         let rtl = (this._slider.get_text_direction() == Clutter.TextDirection.RTL); | ||||
|         let [x, y] = this._slider.get_transformed_position(); | ||||
|         let [w, h] = this._slider.get_transformed_size(); | ||||
|  | ||||
|         let x1 = Math.round(x); | ||||
|         if (rtl) | ||||
|             x1 += Math.round(w); | ||||
|  | ||||
|         let x2 = x1; | ||||
|         let y1 = Math.round(y); | ||||
|         let y2 = y1 + Math.round(h); | ||||
|  | ||||
|         if (this._horizontalBarrier && | ||||
|             this._horizontalBarrier.x1 == x1 && | ||||
|             this._horizontalBarrier.y1 == y1 && | ||||
|             this._horizontalBarrier.x2 == x2 && | ||||
|             this._horizontalBarrier.y2 == y2) | ||||
|             return; | ||||
|  | ||||
|         this._unsetBarrier(); | ||||
|  | ||||
|         let directions = (rtl ? Meta.BarrierDirection.NEGATIVE_X : Meta.BarrierDirection.POSITIVE_X); | ||||
|         this._horizontalBarrier = new Meta.Barrier({ display: global.display, | ||||
|                                                      x1: x1, x2: x2, | ||||
|                                                      y1: y1, y2: y2, | ||||
|                                                      directions: directions }); | ||||
|         this._pressureBarrier.addBarrier(this._horizontalBarrier); | ||||
|     }, | ||||
|  | ||||
|     _unsetBarrier: function() { | ||||
|         if (this._horizontalBarrier == null) | ||||
|             return; | ||||
|  | ||||
|         this._pressureBarrier.removeBarrier(this._horizontalBarrier); | ||||
|         this._horizontalBarrier.destroy(); | ||||
|         this._horizontalBarrier = null; | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         // FIXME: we no longer treat tray icons as notifications | ||||
|         let allowed = Main.sessionMode.hasNotifications; | ||||
| @@ -162,17 +254,20 @@ const LegacyTray = new Lang.Class({ | ||||
|             let [, boxWidth] = this._box.get_preferred_width(-1); | ||||
|             let [, handleWidth] = this._revealHandle.get_preferred_width(-1); | ||||
|  | ||||
|             targetSlide = handleWidth / boxWidth; | ||||
|             if (!this._revealHandle.hover) | ||||
|                 targetSlide *= CONCEALED_VISIBLE_FRACTION; | ||||
|             if (this._revealHandle.hover) | ||||
|                 targetSlide = handleWidth / boxWidth; | ||||
|             else | ||||
|                 targetSlide = CONCEALED_WIDTH / boxWidth; | ||||
|         } | ||||
|  | ||||
|         if (this.actor.visible) | ||||
|         if (this.actor.visible) { | ||||
|             Tweener.addTween(this._slideLayout, | ||||
|                              { slideX: targetSlide, | ||||
|                                time: REVEAL_ANIMATION_TIME, | ||||
|                                transition: 'easeOutQuad' }); | ||||
|         else | ||||
|         } else { | ||||
|             this._slideLayout.slideX = targetSlide; | ||||
|             this._unsetBarrier(); | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -4,6 +4,7 @@ const Atspi = imports.gi.Atspi; | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GDesktopEnums = imports.gi.GDesktopEnums; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
| const Lang = imports.lang; | ||||
| @@ -22,6 +23,8 @@ const MOUSE_POLL_FREQUENCY = 50; | ||||
| const CROSSHAIRS_CLIP_SIZE = [100, 100]; | ||||
| const NO_CHANGE = 0.0; | ||||
|  | ||||
| const POINTER_REST_TIME = 1000; // milliseconds | ||||
|  | ||||
| // Settings | ||||
| const APPLICATIONS_SCHEMA       = 'org.gnome.desktop.a11y.applications'; | ||||
| const SHOW_KEY                  = 'screen-magnifier-enabled'; | ||||
| @@ -444,55 +447,6 @@ const Magnifier = new Lang.Class({ | ||||
|         this._appSettings = new Gio.Settings({ schema_id: APPLICATIONS_SCHEMA }); | ||||
|         this._settings = new Gio.Settings({ schema_id: MAGNIFIER_SCHEMA }); | ||||
|  | ||||
|         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); | ||||
|  | ||||
|             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); | ||||
|         } | ||||
|  | ||||
|         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)); | ||||
| @@ -561,6 +515,56 @@ const Magnifier = new Lang.Class({ | ||||
|                                Lang.bind(this, function() { | ||||
|             this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY)); | ||||
|         })); | ||||
|  | ||||
|         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); | ||||
|  | ||||
|             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); | ||||
|         } | ||||
|  | ||||
|         let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY); | ||||
|         this.addCrosshairs(); | ||||
|         this.setCrosshairsVisible(showCrosshairs); | ||||
|  | ||||
|         return this._appSettings.get_boolean(SHOW_KEY); | ||||
|    }, | ||||
|  | ||||
| @@ -708,6 +712,9 @@ const ZoomRegion = new Lang.Class({ | ||||
|         this._xCaret = 0; | ||||
|         this._yCaret = 0; | ||||
|  | ||||
|         this._pointerIdleMonitor = Meta.IdleMonitor.get_for_device(Meta.VIRTUAL_CORE_POINTER_ID); | ||||
|         this._scrollContentsTimerId = 0; | ||||
|  | ||||
|         Main.layoutManager.connect('monitors-changed', | ||||
|                                    Lang.bind(this, this._monitorsChanged)); | ||||
|         this._focusCaretTracker.connect('caret-moved', | ||||
| @@ -1067,6 +1074,26 @@ const ZoomRegion = new Lang.Class({ | ||||
|         return this._isMouseOverRegion(); | ||||
|     }, | ||||
|  | ||||
|     _clearScrollContentsTimer: function() { | ||||
|         if (this._scrollContentsTimerId != 0) { | ||||
|             Mainloop.source_remove(this._scrollContentsTimerId); | ||||
|             this._scrollContentsTimerId = 0; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _scrollContentsToDelayed: function(x, y) { | ||||
|         if (this._pointerIdleMonitor.get_idletime() >= POINTER_REST_TIME) { | ||||
|             this.scrollContentsTo(x, y); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._clearScrollContentsTimer(); | ||||
|         this._scrollContentsTimerId = Mainloop.timeout_add(POINTER_REST_TIME, Lang.bind(this, function() { | ||||
|             this._scrollContentsToDelayed(x, y); | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
|      * scrollContentsTo: | ||||
|      * Shift the contents of the magnified view such it is centered on the given | ||||
| @@ -1075,6 +1102,8 @@ const ZoomRegion = new Lang.Class({ | ||||
|      * @y:      The y-coord of the point to center on. | ||||
|      */ | ||||
|     scrollContentsTo: function(x, y) { | ||||
|         this._clearScrollContentsTimer(); | ||||
|  | ||||
|         this._followingCursor = false; | ||||
|         this._changeROI({ xCenter: x, | ||||
|                           yCenter: y }); | ||||
| @@ -1380,7 +1409,7 @@ const ZoomRegion = new Lang.Class({ | ||||
|         else if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.CENTERED) | ||||
|             [xCaret, yCaret] = this._centerFromPointCentered(xCaret, yCaret); | ||||
|  | ||||
|         this.scrollContentsTo(xCaret, yCaret); | ||||
|         this._scrollContentsToDelayed(xCaret, yCaret); | ||||
|     }, | ||||
|  | ||||
|     _centerFromFocusPosition: function() { | ||||
| @@ -1394,7 +1423,7 @@ const ZoomRegion = new Lang.Class({ | ||||
|         else if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.CENTERED) | ||||
|             [xFocus, yFocus] = this._centerFromPointCentered(xFocus, yFocus); | ||||
|  | ||||
|         this.scrollContentsTo(xFocus, yFocus); | ||||
|         this._scrollContentsToDelayed(xFocus, yFocus); | ||||
|     }, | ||||
|  | ||||
|     _centerFromPointPush: function(xPoint, yPoint) { | ||||
|   | ||||
| @@ -78,6 +78,7 @@ let _startDate; | ||||
| let _defaultCssStylesheet = null; | ||||
| let _cssStylesheet = null; | ||||
| let _a11ySettings = null; | ||||
| let _themeResource = null; | ||||
|  | ||||
| function _sessionUpdated() { | ||||
|     if (sessionMode.isPrimary) | ||||
| @@ -137,9 +138,7 @@ function _initializeUI() { | ||||
|     Shell.WindowTracker.get_default(); | ||||
|     Shell.AppUsage.get_default(); | ||||
|  | ||||
|     let resource = Gio.Resource.load(global.datadir + '/gnome-shell-theme.gresource'); | ||||
|     resource._register(); | ||||
|  | ||||
|     reloadThemeResource(); | ||||
|     _loadDefaultStylesheet(); | ||||
|  | ||||
|     // Setup the stage hierarchy early | ||||
| @@ -290,6 +289,14 @@ function setThemeStylesheet(cssStylesheet) { | ||||
|     _cssStylesheet = cssStylesheet ? Gio.File.new_for_path(cssStylesheet) : null; | ||||
| } | ||||
|  | ||||
| function reloadThemeResource() { | ||||
|     if (_themeResource) | ||||
|         _themeResource._unregister(); | ||||
|  | ||||
|     _themeResource = Gio.Resource.load(global.datadir + '/gnome-shell-theme.gresource'); | ||||
|     _themeResource._register(); | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * loadTheme: | ||||
|  * | ||||
| @@ -529,6 +536,7 @@ function activateWindow(window, time, workspaceNum) { | ||||
|     } | ||||
|  | ||||
|     overview.hide(); | ||||
|     panel.closeCalendar(); | ||||
| } | ||||
|  | ||||
| // TODO - replace this timeout with some system to guess when the user might | ||||
|   | ||||
| @@ -69,26 +69,6 @@ const Urgency = { | ||||
|     CRITICAL: 3 | ||||
| }; | ||||
|  | ||||
| function _fixMarkup(text, allowMarkup) { | ||||
|     if (allowMarkup) { | ||||
|         // Support &, ", ', < and >, escape all other | ||||
|         // occurrences of '&'. | ||||
|         let _text = text.replace(/&(?!amp;|quot;|apos;|lt;|gt;)/g, '&'); | ||||
|  | ||||
|         // Support <b>, <i>, and <u>, escape anything else | ||||
|         // so it displays as raw markup. | ||||
|         _text = _text.replace(/<(?!\/?[biu]>)/g, '<'); | ||||
|  | ||||
|         try { | ||||
|             Pango.parse_markup(_text, -1, ''); | ||||
|             return _text; | ||||
|         } catch (e) {} | ||||
|     } | ||||
|  | ||||
|     // !allowMarkup, or invalid markup | ||||
|     return GLib.markup_escape_text(text, -1); | ||||
| } | ||||
|  | ||||
| const FocusGrabber = new Lang.Class({ | ||||
|     Name: 'FocusGrabber', | ||||
|  | ||||
| @@ -147,130 +127,6 @@ const FocusGrabber = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const URLHighlighter = new Lang.Class({ | ||||
|     Name: 'URLHighlighter', | ||||
|  | ||||
|     _init: function(text, lineWrap, allowMarkup) { | ||||
|         if (!text) | ||||
|             text = ''; | ||||
|         this.actor = new St.Label({ reactive: true, style_class: 'url-highlighter' }); | ||||
|         this._linkColor = '#ccccff'; | ||||
|         this.actor.connect('style-changed', Lang.bind(this, function() { | ||||
|             let [hasColor, color] = this.actor.get_theme_node().lookup_color('link-color', false); | ||||
|             if (hasColor) { | ||||
|                 let linkColor = color.to_string().substr(0, 7); | ||||
|                 if (linkColor != this._linkColor) { | ||||
|                     this._linkColor = linkColor; | ||||
|                     this._highlightUrls(); | ||||
|                 } | ||||
|             } | ||||
|         })); | ||||
|         if (lineWrap) { | ||||
|             this.actor.clutter_text.line_wrap = true; | ||||
|             this.actor.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR; | ||||
|             this.actor.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         } | ||||
|  | ||||
|         this.setMarkup(text, allowMarkup); | ||||
|         this.actor.connect('button-press-event', Lang.bind(this, function(actor, event) { | ||||
|             // Don't try to URL highlight when invisible. | ||||
|             // The MessageTray doesn't actually hide us, so | ||||
|             // we need to check for paint opacities as well. | ||||
|             if (!actor.visible || actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             // Keep Notification.actor from seeing this and taking | ||||
|             // a pointer grab, which would block our button-release-event | ||||
|             // handler, if an URL is clicked | ||||
|             return this._findUrlAtPos(event) != -1; | ||||
|         })); | ||||
|         this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) { | ||||
|             if (!actor.visible || actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             let urlId = this._findUrlAtPos(event); | ||||
|             if (urlId != -1) { | ||||
|                 let url = this._urls[urlId].url; | ||||
|                 if (url.indexOf(':') == -1) | ||||
|                     url = 'http://' + url; | ||||
|  | ||||
|                 Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context(0, -1)); | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|         this.actor.connect('motion-event', Lang.bind(this, function(actor, event) { | ||||
|             if (!actor.visible || actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             let urlId = this._findUrlAtPos(event); | ||||
|             if (urlId != -1 && !this._cursorChanged) { | ||||
|                 global.screen.set_cursor(Meta.Cursor.POINTING_HAND); | ||||
|                 this._cursorChanged = true; | ||||
|             } else if (urlId == -1) { | ||||
|                 global.screen.set_cursor(Meta.Cursor.DEFAULT); | ||||
|                 this._cursorChanged = false; | ||||
|             } | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|         this.actor.connect('leave-event', Lang.bind(this, function() { | ||||
|             if (!this.actor.visible || this.actor.get_paint_opacity() == 0) | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             if (this._cursorChanged) { | ||||
|                 this._cursorChanged = false; | ||||
|                 global.screen.set_cursor(Meta.Cursor.DEFAULT); | ||||
|             } | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     setMarkup: function(text, allowMarkup) { | ||||
|         text = text ? _fixMarkup(text, allowMarkup) : ''; | ||||
|         this._text = text; | ||||
|  | ||||
|         this.actor.clutter_text.set_markup(text); | ||||
|         /* clutter_text.text contain text without markup */ | ||||
|         this._urls = Util.findUrls(this.actor.clutter_text.text); | ||||
|         this._highlightUrls(); | ||||
|     }, | ||||
|  | ||||
|     _highlightUrls: function() { | ||||
|         // text here contain markup | ||||
|         let urls = Util.findUrls(this._text); | ||||
|         let markup = ''; | ||||
|         let pos = 0; | ||||
|         for (let i = 0; i < urls.length; i++) { | ||||
|             let url = urls[i]; | ||||
|             let str = this._text.substr(pos, url.pos - pos); | ||||
|             markup += str + '<span foreground="' + this._linkColor + '"><u>' + url.url + '</u></span>'; | ||||
|             pos = url.pos + url.url.length; | ||||
|         } | ||||
|         markup += this._text.substr(pos); | ||||
|         this.actor.clutter_text.set_markup(markup); | ||||
|     }, | ||||
|  | ||||
|     _findUrlAtPos: function(event) { | ||||
|         let success; | ||||
|         let [x, y] = event.get_coords(); | ||||
|         [success, x, y] = this.actor.transform_stage_point(x, y); | ||||
|         let find_pos = -1; | ||||
|         for (let i = 0; i < this.actor.clutter_text.text.length; i++) { | ||||
|             let [success, px, py, line_height] = this.actor.clutter_text.position_to_coords(i); | ||||
|             if (py > y || py + line_height < y || x < px) | ||||
|                 continue; | ||||
|             find_pos = i; | ||||
|         } | ||||
|         if (find_pos != -1) { | ||||
|             for (let i = 0; i < this._urls.length; i++) | ||||
|             if (find_pos >= this._urls[i].pos && | ||||
|                 this._urls[i].pos + this._urls[i].url.length > find_pos) | ||||
|                 return i; | ||||
|         } | ||||
|         return -1; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| // 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. | ||||
| @@ -382,6 +238,8 @@ const NotificationApplicationPolicy = new Lang.Class({ | ||||
|  | ||||
|     _changed: function(settings, key) { | ||||
|         this.emit('policy-changed', key); | ||||
|         if (key == 'enable') | ||||
|             this.emit('enable-changed'); | ||||
|     }, | ||||
|  | ||||
|     _canonicalizeId: function(id) { | ||||
| @@ -473,10 +331,6 @@ const NotificationApplicationPolicy = new Lang.Class({ | ||||
| const Notification = new Lang.Class({ | ||||
|     Name: 'Notification', | ||||
|  | ||||
|     ICON_SIZE: 24, | ||||
|  | ||||
|     IMAGE_SIZE: 125, | ||||
|  | ||||
|     _init: function(source, title, banner, params) { | ||||
|         this.source = source; | ||||
|         this.title = title; | ||||
| @@ -485,63 +339,14 @@ const Notification = new Lang.Class({ | ||||
|         // 'transient' is a reserved keyword in JS, so we have to use an alternate variable name | ||||
|         this.isTransient = false; | ||||
|         this.forFeedback = false; | ||||
|         this.expanded = false; | ||||
|         this.focused = false; | ||||
|         this._acknowledged = false; | ||||
|         this._destroyed = false; | ||||
|         this._customContent = false; | ||||
|         this.bannerBodyText = null; | ||||
|         this.bannerBodyMarkup = false; | ||||
|         this._bannerBodyAdded = false; | ||||
|         this._titleFitsInBannerMode = true; | ||||
|         this._spacing = 0; | ||||
|         this._scrollPolicy = Gtk.PolicyType.AUTOMATIC; | ||||
|         this._soundName = null; | ||||
|         this._soundFile = null; | ||||
|         this._soundPlayed = false; | ||||
|         this.actions = []; | ||||
|  | ||||
|         this.actor = new St.Button({ accessible_role: Atk.Role.NOTIFICATION }); | ||||
|         this.actor.add_style_class_name('notification-unexpanded'); | ||||
|         this.actor._delegate = this; | ||||
|         this.actor.connect('clicked', Lang.bind(this, this.activate)); | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|  | ||||
|         this._table = new St.Table({ style_class: 'notification', | ||||
|                                      reactive: true }); | ||||
|         this._table.connect('style-changed', Lang.bind(this, this._styleChanged)); | ||||
|         this.actor.set_child(this._table); | ||||
|  | ||||
|         // The first line should have the title, followed by the | ||||
|         // banner text, but ellipsized if they won't both fit. We can't | ||||
|         // make St.Table or St.BoxLayout do this the way we want (don't | ||||
|         // show banner at all if title needs to be ellipsized), so we | ||||
|         // use Shell.GenericContainer. | ||||
|         this._bannerBox = new Shell.GenericContainer(); | ||||
|         this._bannerBox.connect('get-preferred-width', Lang.bind(this, this._bannerBoxGetPreferredWidth)); | ||||
|         this._bannerBox.connect('get-preferred-height', Lang.bind(this, this._bannerBoxGetPreferredHeight)); | ||||
|         this._bannerBox.connect('allocate', Lang.bind(this, this._bannerBoxAllocate)); | ||||
|         this._table.add(this._bannerBox, { row: 0, | ||||
|                                            col: 1, | ||||
|                                            col_span: 2, | ||||
|                                            x_expand: false, | ||||
|                                            y_expand: false, | ||||
|                                            y_fill: false }); | ||||
|  | ||||
|         // This is an empty cell that overlaps with this._bannerBox cell to ensure | ||||
|         // that this._bannerBox cell expands horizontally, while not forcing the | ||||
|         // this._imageBin that is also in col: 2 to expand horizontally. | ||||
|         this._table.add(new St.Bin(), { row: 0, | ||||
|                                         col: 2, | ||||
|                                         y_expand: false, | ||||
|                                         y_fill: false }); | ||||
|  | ||||
|         this._titleLabel = new St.Label(); | ||||
|         this._bannerBox.add_actor(this._titleLabel); | ||||
|         this._bannerUrlHighlighter = new URLHighlighter(); | ||||
|         this._bannerLabel = this._bannerUrlHighlighter.actor; | ||||
|         this._bannerBox.add_actor(this._bannerLabel); | ||||
|  | ||||
|         // If called with only one argument we assume the caller | ||||
|         // will call .update() later on. This is the case of | ||||
|         // NotificationDaemon, which wants to use the same code | ||||
| @@ -559,107 +364,25 @@ const Notification = new Lang.Class({ | ||||
|     // the title/banner. If @params.clear is %true, it will also | ||||
|     // remove any additional actors/action buttons previously added. | ||||
|     update: function(title, banner, params) { | ||||
|         params = Params.parse(params, { customContent: false, | ||||
|                                         gicon: null, | ||||
|         params = Params.parse(params, { gicon: null, | ||||
|                                         secondaryGIcon: null, | ||||
|                                         bannerMarkup: false, | ||||
|                                         clear: false, | ||||
|                                         soundName: null, | ||||
|                                         soundFile: null }); | ||||
|  | ||||
|         this._customContent = params.customContent; | ||||
|  | ||||
|         let oldFocus = global.stage.key_focus; | ||||
|  | ||||
|         if (this._icon && (params.gicon || params.clear)) { | ||||
|             this._icon.destroy(); | ||||
|             this._icon = null; | ||||
|         } | ||||
|  | ||||
|         if (this._secondaryIcon && (params.secondaryGIcon || params.clear)) { | ||||
|             this._secondaryIcon.destroy(); | ||||
|             this._secondaryIcon = null; | ||||
|         } | ||||
|  | ||||
|         // We always clear the content area if we don't have custom | ||||
|         // content because it might contain the @banner that didn't | ||||
|         // fit in the banner mode. | ||||
|         if (this._scrollArea && (!this._customContent || params.clear)) { | ||||
|             if (oldFocus && this._scrollArea.contains(oldFocus)) | ||||
|                 this.actor.grab_key_focus(); | ||||
|  | ||||
|             this._scrollArea.destroy(); | ||||
|             this._scrollArea = null; | ||||
|             this._contentArea = null; | ||||
|         } | ||||
|         if (this._actionArea && params.clear) { | ||||
|             if (oldFocus && this._actionArea.contains(oldFocus)) | ||||
|                 this.actor.grab_key_focus(); | ||||
|  | ||||
|             this._actionArea.destroy(); | ||||
|             this._actionArea = null; | ||||
|             this._buttonBox = null; | ||||
|             this.actions = []; | ||||
|         } | ||||
|         if (!this._scrollArea && !this._actionArea) | ||||
|             this._table.remove_style_class_name('multi-line-notification'); | ||||
|  | ||||
|         if (params.gicon) { | ||||
|             this._icon = new St.Icon({ gicon: params.gicon, | ||||
|                                        icon_size: this.ICON_SIZE }); | ||||
|         } else { | ||||
|             this._icon = this.source.createIcon(this.ICON_SIZE); | ||||
|         } | ||||
|  | ||||
|         if (this._icon) { | ||||
|             this._table.add(this._icon, { row: 0, | ||||
|                                           col: 0, | ||||
|                                           x_expand: false, | ||||
|                                           y_expand: false, | ||||
|                                           y_fill: false, | ||||
|                                           y_align: St.Align.START }); | ||||
|         } | ||||
|  | ||||
|         if (params.secondaryGIcon) { | ||||
|             this._secondaryIcon = new St.Icon({ gicon: params.secondaryGIcon, | ||||
|                                                 style_class: 'secondary-icon' }); | ||||
|             this._bannerBox.add_actor(this._secondaryIcon); | ||||
|         } | ||||
|  | ||||
|         this.title = title; | ||||
|         title = title ? _fixMarkup(title.replace(/\n/g, ' '), false) : ''; | ||||
|         this._titleLabel.clutter_text.set_markup('<b>' + title + '</b>'); | ||||
|  | ||||
|         let titleDirection; | ||||
|         if (Pango.find_base_dir(title, -1) == Pango.Direction.RTL) | ||||
|             titleDirection = Clutter.TextDirection.RTL; | ||||
|         else | ||||
|             titleDirection = Clutter.TextDirection.LTR; | ||||
|  | ||||
|         // Let the title's text direction control the overall direction | ||||
|         // of the notification - in case where different scripts are used | ||||
|         // in the notification, this is the right thing for the icon, and | ||||
|         // arguably for action buttons as well. Labels other than the title | ||||
|         // will be allocated at the available width, so that their alignment | ||||
|         // is done correctly automatically. | ||||
|         this._table.set_text_direction(titleDirection); | ||||
|  | ||||
|         // Unless the notification has custom content, we save this.bannerBodyText | ||||
|         // to add it to the content of the notification if the notification is | ||||
|         // expandable due to other elements in its content area or due to the banner | ||||
|         // not fitting fully in the single-line mode. | ||||
|         this.bannerBodyText = this._customContent ? null : banner; | ||||
|         this.bannerBodyText = banner; | ||||
|         this.bannerBodyMarkup = params.bannerMarkup; | ||||
|         this._bannerBodyAdded = false; | ||||
|  | ||||
|         banner = banner ? banner.replace(/\n/g, '  ') : ''; | ||||
|         if (params.gicon || params.clear) | ||||
|             this.gicon = params.gicon; | ||||
|  | ||||
|         this._bannerUrlHighlighter.setMarkup(banner, params.bannerMarkup); | ||||
|         this._bannerLabel.queue_relayout(); | ||||
|         if (params.secondaryGIcon || params.clear) | ||||
|             this.secondaryGIcon = params.secondaryGIcon; | ||||
|  | ||||
|         // Add the bannerBody now if we know for sure we'll need it | ||||
|         if (this.bannerBodyText && this.bannerBodyText.indexOf('\n') > -1) | ||||
|             this._addBannerBody(); | ||||
|         if (params.clear) | ||||
|             this.actions = []; | ||||
|  | ||||
|         if (this._soundName != params.soundName || | ||||
|             this._soundFile != params.soundFile) { | ||||
| @@ -668,168 +391,14 @@ const Notification = new Lang.Class({ | ||||
|             this._soundPlayed = false; | ||||
|         } | ||||
|  | ||||
|         this.updated(); | ||||
|         this.emit('updated', params.clear); | ||||
|     }, | ||||
|  | ||||
|     setIconVisible: function(visible) { | ||||
|         this._icon.visible = visible; | ||||
|     }, | ||||
|  | ||||
|     enableScrolling: function(enableScrolling) { | ||||
|         this._scrollPolicy = enableScrolling ? Gtk.PolicyType.AUTOMATIC : Gtk.PolicyType.NEVER; | ||||
|         if (this._scrollArea) { | ||||
|             this._scrollArea.vscrollbar_policy = this._scrollPolicy; | ||||
|             this._scrollArea.enable_mouse_scrolling = enableScrolling; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _createScrollArea: function() { | ||||
|         this._table.add_style_class_name('multi-line-notification'); | ||||
|         this._scrollArea = new St.ScrollView({ style_class: 'notification-scrollview', | ||||
|                                                vscrollbar_policy: this._scrollPolicy, | ||||
|                                                hscrollbar_policy: Gtk.PolicyType.NEVER, | ||||
|                                                visible: this.expanded }); | ||||
|         this._table.add(this._scrollArea, { row: 1, | ||||
|                                             col: 2 }); | ||||
|         this._updateLastColumnSettings(); | ||||
|         this._contentArea = new St.BoxLayout({ style_class: 'notification-body', | ||||
|                                                vertical: true }); | ||||
|         this._scrollArea.add_actor(this._contentArea); | ||||
|         // If we know the notification will be expandable, we need to add | ||||
|         // the banner text to the body as the first element. | ||||
|         this._addBannerBody(); | ||||
|     }, | ||||
|  | ||||
|     // addActor: | ||||
|     // @actor: actor to add to the body of the notification | ||||
|     // | ||||
|     // Appends @actor to the notification's body | ||||
|     addActor: function(actor, style) { | ||||
|         if (!this._scrollArea) { | ||||
|             this._createScrollArea(); | ||||
|         } | ||||
|  | ||||
|         this._contentArea.add(actor, style ? style : {}); | ||||
|         this.updated(); | ||||
|     }, | ||||
|  | ||||
|     // addBody: | ||||
|     // @text: the text | ||||
|     // @markup: %true if @text contains pango markup | ||||
|     // @style: style to use when adding the actor containing the text | ||||
|     // | ||||
|     // Adds a multi-line label containing @text to the notification. | ||||
|     // | ||||
|     // Return value: the newly-added label | ||||
|     addBody: function(text, markup, style) { | ||||
|         let label = new URLHighlighter(text, true, markup); | ||||
|  | ||||
|         this.addActor(label.actor, style); | ||||
|         return label.actor; | ||||
|     }, | ||||
|  | ||||
|     _addBannerBody: function() { | ||||
|         if (this.bannerBodyText && !this._bannerBodyAdded) { | ||||
|             this._bannerBodyAdded = true; | ||||
|             this.addBody(this.bannerBodyText, this.bannerBodyMarkup); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     // scrollTo: | ||||
|     // @side: St.Side.TOP or St.Side.BOTTOM | ||||
|     // | ||||
|     // Scrolls the content area (if scrollable) to the indicated edge | ||||
|     scrollTo: function(side) { | ||||
|         let adjustment = this._scrollArea.vscroll.adjustment; | ||||
|         if (side == St.Side.TOP) | ||||
|             adjustment.value = adjustment.lower; | ||||
|         else if (side == St.Side.BOTTOM) | ||||
|             adjustment.value = adjustment.upper; | ||||
|     }, | ||||
|  | ||||
|     // setActionArea: | ||||
|     // @actor: the actor | ||||
|     // @props: (option) St.Table child properties | ||||
|     // | ||||
|     // Puts @actor into the action area of the notification, replacing | ||||
|     // the previous contents | ||||
|     setActionArea: function(actor, props) { | ||||
|         if (this._actionArea) { | ||||
|             this._actionArea.destroy(); | ||||
|             this._actionArea = null; | ||||
|             if (this._buttonBox) | ||||
|                 this._buttonBox = null; | ||||
|         } else { | ||||
|             this._addBannerBody(); | ||||
|         } | ||||
|         this._actionArea = actor; | ||||
|         this._actionArea.visible = this.expanded; | ||||
|  | ||||
|         if (!props) | ||||
|             props = {}; | ||||
|         props.row = 2; | ||||
|         props.col = 2; | ||||
|  | ||||
|         this._table.add_style_class_name('multi-line-notification'); | ||||
|         this._table.add(this._actionArea, props); | ||||
|         this._updateLastColumnSettings(); | ||||
|         this.updated(); | ||||
|     }, | ||||
|  | ||||
|     _updateLastColumnSettings: function() { | ||||
|         if (this._scrollArea) | ||||
|             this._table.child_set(this._scrollArea, { col: 1, | ||||
|                                                       col_span: 2 }); | ||||
|         if (this._actionArea) | ||||
|             this._table.child_set(this._actionArea, { col: 1, | ||||
|                                                       col_span: 2 }); | ||||
|     }, | ||||
|  | ||||
|     addButton: function(button, callback) { | ||||
|         if (!this._buttonBox) { | ||||
|             let box = new St.BoxLayout({ style_class: 'notification-actions' }); | ||||
|             this.setActionArea(box, { x_expand: false, | ||||
|                                       y_expand: false, | ||||
|                                       x_fill: false, | ||||
|                                       y_fill: false, | ||||
|                                       x_align: St.Align.END }); | ||||
|             this._buttonBox = box; | ||||
|             global.focus_manager.add_group(this._buttonBox); | ||||
|         } | ||||
|  | ||||
|         this._buttonBox.add(button); | ||||
|         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; | ||||
|     }, | ||||
|  | ||||
|     // addAction: | ||||
|     // @label: the label for the action's button | ||||
|     // @callback: the callback for the action | ||||
|     // | ||||
|     // 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) { | ||||
|         this.actions.push({ label: label, callback: callback }); | ||||
|         let button = new St.Button({ style_class: 'notification-button', | ||||
|                                      label: label, | ||||
|                                      can_focus: true }); | ||||
|  | ||||
|         return this.addButton(button, callback); | ||||
|     }, | ||||
|  | ||||
|     get acknowledged() { | ||||
| @@ -859,132 +428,6 @@ const Notification = new Lang.Class({ | ||||
|         this.forFeedback = forFeedback; | ||||
|     }, | ||||
|  | ||||
|     _styleChanged: function() { | ||||
|         this._spacing = this._table.get_theme_node().get_length('spacing-columns'); | ||||
|     }, | ||||
|  | ||||
|     _bannerBoxGetPreferredWidth: function(actor, forHeight, alloc) { | ||||
|         let [titleMin, titleNat] = this._titleLabel.get_preferred_width(forHeight); | ||||
|         let [bannerMin, bannerNat] = this._bannerLabel.get_preferred_width(forHeight); | ||||
|  | ||||
|         if (this._secondaryIcon) { | ||||
|             let [secondaryIconMin, secondaryIconNat] = this._secondaryIcon.get_preferred_width(forHeight); | ||||
|  | ||||
|             alloc.min_size = secondaryIconMin + this._spacing + titleMin; | ||||
|             alloc.natural_size = secondaryIconNat + this._spacing + titleNat + this._spacing + bannerNat; | ||||
|         } else { | ||||
|             alloc.min_size = titleMin; | ||||
|             alloc.natural_size = titleNat + this._spacing + bannerNat; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _bannerBoxGetPreferredHeight: function(actor, forWidth, alloc) { | ||||
|         [alloc.min_size, alloc.natural_size] = | ||||
|             this._titleLabel.get_preferred_height(forWidth); | ||||
|     }, | ||||
|  | ||||
|     _bannerBoxAllocate: function(actor, box, flags) { | ||||
|         let availWidth = box.x2 - box.x1; | ||||
|  | ||||
|         let [titleMinW, titleNatW] = this._titleLabel.get_preferred_width(-1); | ||||
|         let [titleMinH, titleNatH] = this._titleLabel.get_preferred_height(availWidth); | ||||
|         let [bannerMinW, bannerNatW] = this._bannerLabel.get_preferred_width(availWidth); | ||||
|  | ||||
|         let rtl = (this._table.text_direction == Clutter.TextDirection.RTL); | ||||
|         let x = rtl ? availWidth : 0; | ||||
|  | ||||
|         if (this._secondaryIcon) { | ||||
|             let [iconMinW, iconNatW] = this._secondaryIcon.get_preferred_width(-1); | ||||
|             let [iconMinH, iconNatH] = this._secondaryIcon.get_preferred_height(availWidth); | ||||
|  | ||||
|             let secondaryIconBox = new Clutter.ActorBox(); | ||||
|             let secondaryIconBoxW = Math.min(iconNatW, availWidth); | ||||
|  | ||||
|             // allocate secondary icon box | ||||
|             if (rtl) { | ||||
|                 secondaryIconBox.x1 = x - secondaryIconBoxW; | ||||
|                 secondaryIconBox.x2 = x; | ||||
|                 x = x - (secondaryIconBoxW + this._spacing); | ||||
|             } else { | ||||
|                 secondaryIconBox.x1 = x; | ||||
|                 secondaryIconBox.x2 = x + secondaryIconBoxW; | ||||
|                 x = x + secondaryIconBoxW + this._spacing; | ||||
|             } | ||||
|             secondaryIconBox.y1 = 0; | ||||
|             // Using titleNatH ensures that the secondary icon is centered vertically | ||||
|             secondaryIconBox.y2 = titleNatH; | ||||
|  | ||||
|             availWidth = availWidth - (secondaryIconBoxW + this._spacing); | ||||
|             this._secondaryIcon.allocate(secondaryIconBox, flags); | ||||
|         } | ||||
|  | ||||
|         let titleBox = new Clutter.ActorBox(); | ||||
|         let titleBoxW = Math.min(titleNatW, availWidth); | ||||
|         if (rtl) { | ||||
|             titleBox.x1 = availWidth - titleBoxW; | ||||
|             titleBox.x2 = availWidth; | ||||
|         } else { | ||||
|             titleBox.x1 = x; | ||||
|             titleBox.x2 = titleBox.x1 + titleBoxW; | ||||
|         } | ||||
|         titleBox.y1 = 0; | ||||
|         titleBox.y2 = titleNatH; | ||||
|         this._titleLabel.allocate(titleBox, flags); | ||||
|         this._titleFitsInBannerMode = (titleNatW <= availWidth); | ||||
|  | ||||
|         let bannerFits = true; | ||||
|         if (titleBoxW + this._spacing > availWidth) { | ||||
|             this._bannerLabel.opacity = 0; | ||||
|             bannerFits = false; | ||||
|         } else { | ||||
|             let bannerBox = new Clutter.ActorBox(); | ||||
|  | ||||
|             if (rtl) { | ||||
|                 bannerBox.x1 = 0; | ||||
|                 bannerBox.x2 = titleBox.x1 - this._spacing; | ||||
|  | ||||
|                 bannerFits = (bannerBox.x2 - bannerNatW >= 0); | ||||
|             } else { | ||||
|                 bannerBox.x1 = titleBox.x2 + this._spacing; | ||||
|                 bannerBox.x2 = availWidth; | ||||
|  | ||||
|                 bannerFits = (bannerBox.x1 + bannerNatW <= availWidth); | ||||
|             } | ||||
|             bannerBox.y1 = 0; | ||||
|             bannerBox.y2 = titleNatH; | ||||
|             this._bannerLabel.allocate(bannerBox, flags); | ||||
|  | ||||
|             // Make _bannerLabel visible if the entire notification | ||||
|             // fits on one line, or if the notification is currently | ||||
|             // unexpanded and only showing one line anyway. | ||||
|             if (!this.expanded || (bannerFits && this._table.row_count == 1)) | ||||
|                 this._bannerLabel.opacity = 255; | ||||
|         } | ||||
|  | ||||
|         // If the banner doesn't fully fit in the banner box, we possibly need to add the | ||||
|         // banner to the body. We can't do that from here though since that will force a | ||||
|         // relayout, so we add it to the main loop. | ||||
|         if (!bannerFits && this._canExpandContent()) | ||||
|             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'); | ||||
|                                             this.updated(); | ||||
|                                         } | ||||
|                                         return false; | ||||
|                                      })); | ||||
|     }, | ||||
|  | ||||
|     _canExpandContent: function() { | ||||
|         return (this.bannerBodyText && !this._bannerBodyAdded) || | ||||
|                (!this._titleFitsInBannerMode && !this._table.has_style_class_name('multi-line-notification')); | ||||
|     }, | ||||
|  | ||||
|     playSound: function() { | ||||
|         if (this._soundPlayed) | ||||
|             return; | ||||
| @@ -1017,71 +460,6 @@ const Notification = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     updated: function() { | ||||
|         if (this.expanded) | ||||
|             this.expand(false); | ||||
|     }, | ||||
|  | ||||
|     expand: function(animate) { | ||||
|         this.expanded = true; | ||||
|         this.actor.remove_style_class_name('notification-unexpanded'); | ||||
|  | ||||
|         // Show additional content that we keep hidden in banner mode | ||||
|         if (this._actionArea) | ||||
|             this._actionArea.show(); | ||||
|         if (this._scrollArea) | ||||
|             this._scrollArea.show(); | ||||
|  | ||||
|         // The banner is never shown when the title did not fit, so this | ||||
|         // can be an if-else statement. | ||||
|         if (!this._titleFitsInBannerMode) { | ||||
|             // Remove ellipsization from the title label and make it wrap so that | ||||
|             // we show the full title when the notification is expanded. | ||||
|             this._titleLabel.clutter_text.line_wrap = true; | ||||
|             this._titleLabel.clutter_text.line_wrap_mode = Pango.WrapMode.WORD_CHAR; | ||||
|             this._titleLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         } else if (this._table.row_count > 1 && this._bannerLabel.opacity != 0) { | ||||
|             // We always hide the banner if the notification has additional content. | ||||
|             // | ||||
|             // We don't need to wrap the banner that doesn't fit the way we wrap the | ||||
|             // title that doesn't fit because we won't have a notification with | ||||
|             // row_count=1 that has a banner that doesn't fully fit. We'll either add | ||||
|             // that banner to the content of the notification in _bannerBoxAllocate() | ||||
|             // or the notification will have custom content. | ||||
|             if (animate) | ||||
|                 Tweener.addTween(this._bannerLabel, | ||||
|                                  { opacity: 0, | ||||
|                                    time: ANIMATION_TIME, | ||||
|                                    transition: 'easeOutQuad' }); | ||||
|             else | ||||
|                 this._bannerLabel.opacity = 0; | ||||
|         } | ||||
|         this.emit('expanded'); | ||||
|     }, | ||||
|  | ||||
|     collapseCompleted: function() { | ||||
|         if (this._destroyed) | ||||
|             return; | ||||
|         this.expanded = false; | ||||
|  | ||||
|         // Hide additional content that we keep hidden in banner mode | ||||
|         if (this._actionArea) | ||||
|             this._actionArea.hide(); | ||||
|         if (this._scrollArea) | ||||
|             this._scrollArea.hide(); | ||||
|  | ||||
|         // Make sure we don't line wrap the title, and ellipsize it instead. | ||||
|         this._titleLabel.clutter_text.line_wrap = false; | ||||
|         this._titleLabel.clutter_text.ellipsize = Pango.EllipsizeMode.END; | ||||
|  | ||||
|         // Restore banner opacity in case the notification is shown in the | ||||
|         // banner mode again on update. | ||||
|         this._bannerLabel.opacity = 255; | ||||
|  | ||||
|         // Restore height requisition | ||||
|         this.actor.add_style_class_name('notification-unexpanded'); | ||||
|     }, | ||||
|  | ||||
|     // Allow customizing the banner UI: | ||||
|     // the default implementation defers the creation to | ||||
|     // the source (which will create a NotificationBanner), | ||||
| @@ -1093,27 +471,14 @@ const Notification = new Lang.Class({ | ||||
|  | ||||
|     activate: function() { | ||||
|         this.emit('activated'); | ||||
|         // We hide all types of notifications once the user clicks on them because the common | ||||
|         // outcome of clicking should be the relevant window being brought forward and the user's | ||||
|         // attention switching to the window. | ||||
|         this.emit('done-displaying'); | ||||
|         if (!this.resident) | ||||
|             this.destroy(); | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
|         if (this._destroyed) | ||||
|             return; | ||||
|         this._destroyed = true; | ||||
|         if (!this._destroyedReason) | ||||
|             this._destroyedReason = NotificationDestroyedReason.DISMISSED; | ||||
|         this.emit('destroy', this._destroyedReason); | ||||
|     }, | ||||
|  | ||||
|     destroy: function(reason) { | ||||
|         this._destroyedReason = reason; | ||||
|         this.actor.destroy(); | ||||
|         this.actor._delegate = null; | ||||
|         if (!reason) | ||||
|             reason = NotificationDestroyedReason.DISMISSED; | ||||
|         this.emit('destroy', reason); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(Notification.prototype); | ||||
| @@ -1125,8 +490,8 @@ const NotificationBanner = new Lang.Class({ | ||||
|     _init: function(notification) { | ||||
|         this.parent(notification); | ||||
|  | ||||
|         this.actor.can_focus = false; | ||||
|         this.actor.add_style_class_name('notification-banner'); | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroyed)); | ||||
|  | ||||
|         this._buttonBox = null; | ||||
|  | ||||
| @@ -1143,7 +508,8 @@ const NotificationBanner = new Lang.Class({ | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     _onDestroyed: function() { | ||||
|     _onDestroy: function() { | ||||
|         this.parent(); | ||||
|         this.notification.disconnect(this._activatedId); | ||||
|     }, | ||||
|  | ||||
| @@ -1370,10 +736,6 @@ const Source = new Lang.Class({ | ||||
|         return this.count > 1; | ||||
|     }, | ||||
|  | ||||
|     get isClearable() { | ||||
|         return !this.isChat && !this.resident; | ||||
|     }, | ||||
|  | ||||
|     countUpdated: function() { | ||||
|         this.emit('count-updated'); | ||||
|     }, | ||||
| @@ -1546,8 +908,6 @@ const MessageTray = new Lang.Class({ | ||||
|         this._notificationTimeoutId = 0; | ||||
|         this._notificationRemoved = false; | ||||
|  | ||||
|         this.clearableCount = 0; | ||||
|  | ||||
|         Main.layoutManager.addChrome(this.actor, { affectsInputRegion: false }); | ||||
|         Main.layoutManager.trackChrome(this._bannerBin, { affectsInputRegion: true }); | ||||
|  | ||||
| @@ -1598,6 +958,14 @@ const MessageTray = new Lang.Class({ | ||||
|         Shell.util_set_hidden_from_pick(this.actor, false); | ||||
|     }, | ||||
|  | ||||
|     get bannerAlignment() { | ||||
|         return this._bannerBin.get_x_align(); | ||||
|     }, | ||||
|  | ||||
|     set bannerAlignment(align) { | ||||
|         this._bannerBin.set_x_align(align); | ||||
|     }, | ||||
|  | ||||
|     _onNotificationKeyRelease: function(actor, event) { | ||||
|         if (event.get_key_symbol() == Clutter.KEY_Escape && event.get_state() == 0) { | ||||
|             this._expireNotification(); | ||||
| @@ -1648,9 +1016,6 @@ const MessageTray = new Lang.Class({ | ||||
|             destroyId: 0, | ||||
|         }; | ||||
|  | ||||
|         if (source.isClearable) | ||||
|             this.clearableCount++; | ||||
|  | ||||
|         this._sources.set(source, obj); | ||||
|  | ||||
|         obj.notifyId = source.connect('notify', Lang.bind(this, this._onNotify)); | ||||
| @@ -1663,9 +1028,6 @@ const MessageTray = new Lang.Class({ | ||||
|         let obj = this._sources.get(source); | ||||
|         this._sources.delete(source); | ||||
|  | ||||
|         if (source.isClearable) | ||||
|             this.clearableCount--; | ||||
|  | ||||
|         source.disconnect(obj.notifyId); | ||||
|         source.disconnect(obj.destroyId); | ||||
|  | ||||
| @@ -1937,11 +1299,7 @@ const MessageTray = new Lang.Class({ | ||||
|             this.idleMonitor.add_user_active_watch(Lang.bind(this, this._onIdleMonitorBecameActive)); | ||||
|         } | ||||
|  | ||||
|         // HACK: didn't manage to get chat into a proper state in time | ||||
|         if (this._notification.source.isChat) | ||||
|             this._banner = this._notification; | ||||
|         else | ||||
|             this._banner = this._notification.createBanner(); | ||||
|         this._banner = this._notification.createBanner(); | ||||
|         this._bannerClickedId = this._banner.connect('done-displaying', | ||||
|                                                      Lang.bind(this, this._escapeTray)); | ||||
|         this._bannerUnfocusedId = this._banner.connect('unfocused', Lang.bind(this, function() { | ||||
| @@ -2093,10 +1451,7 @@ const MessageTray = new Lang.Class({ | ||||
|         this._pointerInNotification = false; | ||||
|         this._notificationRemoved = false; | ||||
|  | ||||
|         if (notification.resident) | ||||
|             this._bannerBin.remove_actor(this._banner.actor); | ||||
|         else | ||||
|             this._banner.actor.destroy(); | ||||
|         this._banner.actor.destroy(); | ||||
|         this._banner = null; | ||||
|         this.actor.hide(); | ||||
|     }, | ||||
|   | ||||
| @@ -14,7 +14,6 @@ const Atk = imports.gi.Atk; | ||||
|  | ||||
| const Params = imports.misc.params; | ||||
|  | ||||
| const Animation = imports.ui.animation; | ||||
| const Layout = imports.ui.layout; | ||||
| const Lightbox = imports.ui.lightbox; | ||||
| const Main = imports.ui.main; | ||||
| @@ -23,10 +22,6 @@ const Tweener = imports.ui.tweener; | ||||
| const OPEN_AND_CLOSE_TIME = 0.1; | ||||
| const FADE_OUT_DIALOG_TIME = 1.0; | ||||
|  | ||||
| const WORK_SPINNER_ICON_SIZE = 24; | ||||
| const WORK_SPINNER_ANIMATION_DELAY = 1.0; | ||||
| const WORK_SPINNER_ANIMATION_TIME = 0.3; | ||||
|  | ||||
| const State = { | ||||
|     OPENED: 0, | ||||
|     CLOSED: 1, | ||||
| @@ -79,7 +74,9 @@ const ModalDialog = new Lang.Class({ | ||||
|         this._group.add_actor(this._backgroundBin); | ||||
|  | ||||
|         this.dialogLayout = new St.BoxLayout({ style_class: 'modal-dialog', | ||||
|                                                vertical:    true }); | ||||
|                                                x_align:      Clutter.ActorAlign.CENTER, | ||||
|                                                y_align:      Clutter.ActorAlign.CENTER, | ||||
|                                                vertical:     true }); | ||||
|         // modal dialogs are fixed width and grow vertically; set the request | ||||
|         // mode accordingly so wrapped labels are handled correctly during | ||||
|         // size requests. | ||||
| @@ -100,7 +97,8 @@ const ModalDialog = new Lang.Class({ | ||||
|         this.backgroundStack.add_actor(this.dialogLayout); | ||||
|  | ||||
|  | ||||
|         this.contentLayout = new St.BoxLayout({ vertical: true }); | ||||
|         this.contentLayout = new St.BoxLayout({ vertical: true, | ||||
|                                                 style_class: "modal-dialog-content-box" }); | ||||
|         this.dialogLayout.add(this.contentLayout, | ||||
|                               { expand:  true, | ||||
|                                 x_fill:  true, | ||||
| @@ -108,8 +106,7 @@ const ModalDialog = new Lang.Class({ | ||||
|                                 x_align: St.Align.MIDDLE, | ||||
|                                 y_align: St.Align.START }); | ||||
|  | ||||
|         this.buttonLayout = new St.BoxLayout({ style_class: 'modal-dialog-button-box', | ||||
|                                                vertical: false }); | ||||
|         this.buttonLayout = new St.Widget ({ layout_manager: new Clutter.BoxLayout ({ homogeneous:true }) }); | ||||
|         this.dialogLayout.add(this.buttonLayout, | ||||
|                               { x_align: St.Align.MIDDLE, | ||||
|                                 y_align: St.Align.END }); | ||||
| @@ -118,8 +115,6 @@ const ModalDialog = new Lang.Class({ | ||||
|         this._initialKeyFocus = this.dialogLayout; | ||||
|         this._initialKeyFocusDestroyId = 0; | ||||
|         this._savedKeyFocus = null; | ||||
|  | ||||
|         this._workSpinner = null; | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
| @@ -147,16 +142,12 @@ const ModalDialog = new Lang.Class({ | ||||
|             else | ||||
|                 x_alignment = St.Align.MIDDLE; | ||||
|  | ||||
|             this.addButton(buttonInfo, { expand: true, | ||||
|                                          x_fill: false, | ||||
|                                          y_fill: false, | ||||
|                                          x_align: x_alignment, | ||||
|                                          y_align: St.Align.MIDDLE }); | ||||
|             this.addButton(buttonInfo); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     addButton: function(buttonInfo, layoutInfo) { | ||||
|         let label = buttonInfo['label']; | ||||
|     addButton: function(buttonInfo) { | ||||
|         let label = buttonInfo['label'] | ||||
|         let action = buttonInfo['action']; | ||||
|         let key = buttonInfo['key']; | ||||
|         let isDefault = buttonInfo['default']; | ||||
| @@ -170,10 +161,12 @@ const ModalDialog = new Lang.Class({ | ||||
|         else | ||||
|             keys = []; | ||||
|  | ||||
|         let button = new St.Button({ style_class: 'modal-dialog-button button', | ||||
|         let button = new St.Button({ style_class: 'modal-dialog-linked-button', | ||||
|                                      button_mask: St.ButtonMask.ONE | St.ButtonMask.THREE, | ||||
|                                      reactive:    true, | ||||
|                                      can_focus:   true, | ||||
|                                      x_expand:    true, | ||||
|                                      y_expand:    true, | ||||
|                                      label:       label }); | ||||
|         button.connect('clicked', action); | ||||
|  | ||||
| @@ -188,47 +181,11 @@ const ModalDialog = new Lang.Class({ | ||||
|         for (let i in keys) | ||||
|             this._buttonKeys[keys[i]] = buttonInfo; | ||||
|  | ||||
|         this.buttonLayout.add(button, layoutInfo); | ||||
|         this.buttonLayout.add_actor(button); | ||||
|  | ||||
|         return button; | ||||
|     }, | ||||
|  | ||||
|     placeSpinner: function(layoutInfo) { | ||||
|         let spinnerIcon = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg'); | ||||
|         this._workSpinner = new Animation.AnimatedIcon(spinnerIcon, WORK_SPINNER_ICON_SIZE); | ||||
|         this._workSpinner.actor.opacity = 0; | ||||
|         this._workSpinner.actor.show(); | ||||
|  | ||||
|         this.buttonLayout.add(this._workSpinner.actor, layoutInfo); | ||||
|     }, | ||||
|  | ||||
|     setWorking: function(working) { | ||||
|         if (!this._workSpinner) | ||||
|             return; | ||||
|  | ||||
|         Tweener.removeTweens(this._workSpinner.actor); | ||||
|         if (working) { | ||||
|             this._workSpinner.play(); | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 255, | ||||
|                                delay: WORK_SPINNER_ANIMATION_DELAY, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear' | ||||
|                              }); | ||||
|         } else { | ||||
|             Tweener.addTween(this._workSpinner.actor, | ||||
|                              { opacity: 0, | ||||
|                                time: WORK_SPINNER_ANIMATION_TIME, | ||||
|                                transition: 'linear', | ||||
|                                onCompleteScope: this, | ||||
|                                onComplete: function() { | ||||
|                                    if (this._workSpinner) | ||||
|                                        this._workSpinner.stop(); | ||||
|                                } | ||||
|                              }); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onKeyPressEvent: function(object, event) { | ||||
|         this._pressedKey = event.get_key_symbol(); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|   | ||||
| @@ -15,8 +15,7 @@ const OsdMonitorLabel = new Lang.Class({ | ||||
|     Name: 'OsdMonitorLabel', | ||||
|  | ||||
|     _init: function(monitor, label) { | ||||
|         this._actor = new St.Widget({ opacity: 0, | ||||
|                                       x_expand: true, | ||||
|         this._actor = new St.Widget({ x_expand: true, | ||||
|                                       y_expand: true }); | ||||
|  | ||||
|         this._monitor = monitor; | ||||
| @@ -34,10 +33,6 @@ const OsdMonitorLabel = new Lang.Class({ | ||||
|         this._position(); | ||||
|  | ||||
|         Meta.disable_unredirect_for_screen(global.screen); | ||||
|         Tweener.addTween(this._actor, | ||||
|                          { opacity: 255, | ||||
|                            time: FADE_TIME, | ||||
|                            transition: 'easeOutQuad' }); | ||||
|     }, | ||||
|  | ||||
|     _position: function() { | ||||
| @@ -52,15 +47,8 @@ const OsdMonitorLabel = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
|         Tweener.addTween(this._actor, | ||||
|                          { opacity: 0, | ||||
|                            time: FADE_TIME, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onComplete: Lang.bind(this, function() { | ||||
|                                this._actor.destroy(); | ||||
|                                Meta.enable_unredirect_for_screen(global.screen); | ||||
|                            }) | ||||
|                          }); | ||||
|         this._actor.destroy(); | ||||
|         Meta.enable_unredirect_for_screen(global.screen); | ||||
|     } | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -107,13 +107,6 @@ const Overview = new Lang.Class({ | ||||
|  | ||||
|         this._overviewCreated = true; | ||||
|  | ||||
|         // The main Background actors are inside global.window_group which are | ||||
|         // hidden when displaying the overview, so we create a new | ||||
|         // one. Instances of this class share a single CoglTexture behind the | ||||
|         // scenes which allows us to show the background with different | ||||
|         // rendering options without duplicating the texture data. | ||||
|         let monitor = Main.layoutManager.primaryMonitor; | ||||
|  | ||||
|         let layout = new Clutter.BinLayout(); | ||||
|         this._stack = new Clutter.Actor({ layout_manager: layout }); | ||||
|         this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true })); | ||||
| @@ -128,6 +121,11 @@ const Overview = new Lang.Class({ | ||||
|                                             y_expand: true }); | ||||
|         this._overview._delegate = this; | ||||
|  | ||||
|         // The main Background actors are inside global.window_group which are | ||||
|         // hidden when displaying the overview, so we create a new | ||||
|         // one. Instances of this class share a single CoglTexture behind the | ||||
|         // scenes which allows us to show the background with different | ||||
|         // rendering options without duplicating the texture data. | ||||
|         this._backgroundGroup = new Meta.BackgroundGroup(); | ||||
|         Main.layoutManager.overviewGroup.add_child(this._backgroundGroup); | ||||
|         this._bgManagers = []; | ||||
|   | ||||
							
								
								
									
										102
									
								
								js/ui/panel.js
									
									
									
									
									
								
							
							
						
						| @@ -25,12 +25,12 @@ const RemoteMenu = imports.ui.remoteMenu; | ||||
| const Main = imports.ui.main; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const PANEL_ICON_SIZE = 24; | ||||
| const APP_MENU_ICON_MARGIN = 2; | ||||
| const PANEL_ICON_SIZE = 16; | ||||
| const APP_MENU_ICON_MARGIN = 0; | ||||
|  | ||||
| const BUTTON_DND_ACTIVATION_TIMEOUT = 250; | ||||
|  | ||||
| const SPINNER_ANIMATION_TIME = 0.2; | ||||
| const SPINNER_ANIMATION_TIME = 1.0; | ||||
|  | ||||
| // To make sure the panel corners blend nicely with the panel, | ||||
| // we draw background and borders the same way, e.g. drawing | ||||
| @@ -95,6 +95,7 @@ const AppMenuButton = new Lang.Class({ | ||||
|         this._startingApps = []; | ||||
|  | ||||
|         this._menuManager = panel.menuManager; | ||||
|         this._gtkSettings = Gtk.Settings.get_default(); | ||||
|         this._targetApp = null; | ||||
|         this._appMenuNotifyId = 0; | ||||
|         this._actionGroupNotifyId = 0; | ||||
| @@ -123,11 +124,14 @@ const AppMenuButton = new Lang.Class({ | ||||
|         this._arrow = PopupMenu.arrowIcon(St.Side.BOTTOM); | ||||
|         this._container.add_actor(this._arrow); | ||||
|  | ||||
|         this._visible = !Main.overview.visible; | ||||
|         this._visible = this._gtkSettings.gtk_shell_shows_app_menu && | ||||
|                         !Main.overview.visible; | ||||
|         if (!this._visible) | ||||
|             this.actor.hide(); | ||||
|         this._overviewHidingId = Main.overview.connect('hiding', Lang.bind(this, this._sync)); | ||||
|         this._overviewShowingId = Main.overview.connect('showing', Lang.bind(this, this._sync)); | ||||
|         this._showsAppMenuId = this._gtkSettings.connect('notify::gtk-shell-shows-app-menu', | ||||
|                                                          Lang.bind(this, this._sync)); | ||||
|  | ||||
|         this._stop = true; | ||||
|  | ||||
| @@ -305,7 +309,9 @@ const AppMenuButton = new Lang.Class({ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         let visible = (this._targetApp != null && !Main.overview.visibleTarget); | ||||
|         let visible = (this._targetApp != null && | ||||
|                        this._gtkSettings.gtk_shell_shows_app_menu && | ||||
|                        !Main.overview.visibleTarget); | ||||
|         if (visible) | ||||
|             this.show(); | ||||
|         else | ||||
| @@ -378,6 +384,10 @@ const AppMenuButton = new Lang.Class({ | ||||
|             Main.overview.disconnect(this._overviewShowingId); | ||||
|             this._overviewShowingId = 0; | ||||
|         } | ||||
|         if (this._showsAppMenuId > 0) { | ||||
|             this._gtkSettings.disconnect(this._showsAppMenuId); | ||||
|             this._showsAppMenuId = 0; | ||||
|         } | ||||
|         if (this._switchWorkspaceNotifyId > 0) { | ||||
|             global.window_manager.disconnect(this._switchWorkspaceNotifyId); | ||||
|             this._switchWorkspaceNotifyId = 0; | ||||
| @@ -449,7 +459,8 @@ const ActivitiesButton = new Lang.Class({ | ||||
|  | ||||
|         if (event.type() == Clutter.EventType.TOUCH_END || | ||||
|             event.type() == Clutter.EventType.BUTTON_RELEASE) | ||||
|             Main.overview.toggle(); | ||||
|             if (Main.overview.shouldToggleByCornerOrButton()) | ||||
|                 Main.overview.toggle(); | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
| @@ -457,7 +468,8 @@ const ActivitiesButton = new Lang.Class({ | ||||
|     _onKeyRelease: function(actor, event) { | ||||
|         let symbol = event.get_key_symbol(); | ||||
|         if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) { | ||||
|             Main.overview.toggle(); | ||||
|             if (Main.overview.shouldToggleByCornerOrButton()) | ||||
|                 Main.overview.toggle(); | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
| @@ -642,14 +654,50 @@ const PanelCorner = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const AggregateLayout = new Lang.Class({ | ||||
|     Name: 'AggregateLayout', | ||||
|     Extends: Clutter.BoxLayout, | ||||
|  | ||||
|     _init: function(params) { | ||||
|         if (!params) | ||||
|             params = {}; | ||||
|         params['orientation'] = Clutter.Orientation.VERTICAL; | ||||
|         this.parent(params); | ||||
|  | ||||
|         this._sizeChildren = []; | ||||
|     }, | ||||
|  | ||||
|     addSizeChild: function(actor) { | ||||
|         this._sizeChildren.push(actor); | ||||
|         this.layout_changed(); | ||||
|     }, | ||||
|  | ||||
|     vfunc_get_preferred_width: function(container, forHeight) { | ||||
|         let themeNode = container.get_theme_node(); | ||||
|         let minWidth = themeNode.get_min_width(); | ||||
|         let natWidth = minWidth; | ||||
|  | ||||
|         for (let i = 0; i < this._sizeChildren.length; i++) { | ||||
|             let child = this._sizeChildren[i]; | ||||
|             let [childMin, childNat] = child.get_preferred_width(forHeight); | ||||
|             minWidth = Math.max(minWidth, childMin); | ||||
|             natWidth = Math.max(minWidth, childNat); | ||||
|         } | ||||
|         return [minWidth, natWidth]; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const AggregateMenu = new Lang.Class({ | ||||
|     Name: 'AggregateMenu', | ||||
|     Extends: PanelMenu.Button, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent(0.0, _("Settings"), false); | ||||
|         this.parent(0.0, C_("System menu in the top bar", "System"), false); | ||||
|         this.menu.actor.add_style_class_name('aggregate-menu'); | ||||
|  | ||||
|         let menuLayout = new AggregateLayout(); | ||||
|         this.menu.box.set_layout_manager(menuLayout); | ||||
|  | ||||
|         this._indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box' }); | ||||
|         this.actor.add_child(this._indicators); | ||||
|  | ||||
| @@ -697,8 +745,12 @@ const AggregateMenu = new Lang.Class({ | ||||
|         this.menu.addMenuItem(this._location.menu); | ||||
|         this.menu.addMenuItem(this._rfkill.menu); | ||||
|         this.menu.addMenuItem(this._power.menu); | ||||
|         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this.menu.addMenuItem(this._system.menu); | ||||
|  | ||||
|         menuLayout.addSizeChild(this._location.menu.actor); | ||||
|         menuLayout.addSizeChild(this._rfkill.menu.actor); | ||||
|         menuLayout.addSizeChild(this._power.menu.actor); | ||||
|         menuLayout.addSizeChild(this._system.menu.actor); | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| @@ -752,7 +804,7 @@ const Panel = new Lang.Class({ | ||||
|         })); | ||||
|  | ||||
|         Main.layoutManager.panelBox.add(this.actor); | ||||
|         Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'emblem-system-symbolic', | ||||
|         Main.ctrlAltTabManager.addGroup(this.actor, _("Top Bar"), 'focus-top-bar-symbolic', | ||||
|                                         { sortGroup: CtrlAltTab.SortGroup.TOP }); | ||||
|  | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._updatePanel)); | ||||
| @@ -933,6 +985,14 @@ const Panel = new Lang.Class({ | ||||
|         this._updateBox(panel.center, this._centerBox); | ||||
|         this._updateBox(panel.right, this._rightBox); | ||||
|  | ||||
|         if (panel.left.indexOf('dateMenu') != -1) | ||||
|             Main.messageTray.bannerAlignment = Clutter.ActorAlign.START; | ||||
|         else if (panel.right.indexOf('dateMenu') != -1) | ||||
|             Main.messageTray.bannerAlignment = Clutter.ActorAlign.END; | ||||
|         // Default to center if there is no dateMenu | ||||
|         else | ||||
|             Main.messageTray.bannerAlignment = Clutter.ActorAlign.CENTER; | ||||
|  | ||||
|         if (this._sessionStyle) | ||||
|             this._removeStyleClassName(this._sessionStyle); | ||||
|  | ||||
| @@ -995,6 +1055,7 @@ const Panel = new Lang.Class({ | ||||
|         if (parent) | ||||
|             parent.remove_actor(container); | ||||
|  | ||||
|  | ||||
|         box.insert_child_at_index(container, position); | ||||
|         if (indicator.menu) | ||||
|             this.menuManager.addMenu(indicator.menu); | ||||
| @@ -1004,6 +1065,8 @@ const Panel = new Lang.Class({ | ||||
|             emitter.disconnect(destroyId); | ||||
|             container.destroy(); | ||||
|         })); | ||||
|         indicator.connect('menu-set', Lang.bind(this, this._onMenuSet)); | ||||
|         this._onMenuSet(indicator); | ||||
|     }, | ||||
|  | ||||
|     addToStatusArea: function(role, indicator, position, box) { | ||||
| @@ -1035,5 +1098,24 @@ const Panel = new Lang.Class({ | ||||
|         this.actor.remove_style_class_name(className); | ||||
|         this._rightCorner.actor.remove_style_class_name(className); | ||||
|         this._leftCorner.actor.remove_style_class_name(className); | ||||
|     }, | ||||
|  | ||||
|     _onMenuSet: function(indicator) { | ||||
|         if (!indicator.menu || indicator.menu._openChangedId > 0) | ||||
|             return; | ||||
|  | ||||
|         indicator.menu._openChangedId = indicator.menu.connect('open-state-changed', | ||||
|             Lang.bind(this, function(menu, isOpen) { | ||||
|                 let boxAlignment; | ||||
|                 if (this._leftBox.contains(indicator.container)) | ||||
|                     boxAlignment = Clutter.ActorAlign.START; | ||||
|                 else if (this._centerBox.contains(indicator.container)) | ||||
|                     boxAlignment = Clutter.ActorAlign.CENTER; | ||||
|                 else if (this._rightBox.contains(indicator.container)) | ||||
|                     boxAlignment = Clutter.ActorAlign.END; | ||||
|  | ||||
|                 if (boxAlignment == Main.messageTray.bannerAlignment) | ||||
|                     Main.messageTray.bannerBlocked = isOpen; | ||||
|             })); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -128,6 +128,7 @@ const Button = new Lang.Class({ | ||||
|             Main.uiGroup.add_actor(this.menu.actor); | ||||
|             this.menu.actor.hide(); | ||||
|         } | ||||
|         this.emit('menu-set'); | ||||
|     }, | ||||
|  | ||||
|     _onEvent: function(actor, event) { | ||||
|   | ||||
| @@ -12,7 +12,6 @@ const BoxPointer = imports.ui.boxpointer; | ||||
| const GrabHelper = imports.ui.grabHelper; | ||||
| const Main = imports.ui.main; | ||||
| const Params = imports.misc.params; | ||||
| const Separator = imports.ui.separator; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const Ornament = { | ||||
| @@ -170,10 +169,10 @@ const PopupBaseMenuItem = new Lang.Class({ | ||||
|         if (activeChanged) { | ||||
|             this.active = active; | ||||
|             if (active) { | ||||
|                 this.actor.add_style_class_name('active'); | ||||
|                 this.actor.add_style_class_name('selected'); | ||||
|                 this.actor.grab_key_focus(); | ||||
|             } else { | ||||
|                 this.actor.remove_style_class_name('active'); | ||||
|                 this.actor.remove_style_class_name('selected'); | ||||
|                 // Remove the CSS active state if the user press the button and | ||||
|                 // while holding moves to another menu item, so we don't paint all items. | ||||
|                 // The correct behaviour would be to set the new item with the CSS | ||||
| @@ -264,7 +263,9 @@ const PopupSeparatorMenuItem = new Lang.Class({ | ||||
|                            Lang.bind(this, this._syncVisibility)); | ||||
|         this._syncVisibility(); | ||||
|  | ||||
|         this._separator = new St.Widget({ style_class: 'popup-separator-menu-item' }); | ||||
|         this._separator = new St.Widget({ style_class: 'popup-separator-menu-item', | ||||
|                                           y_expand: true, | ||||
|                                           y_align: Clutter.ActorAlign.CENTER }); | ||||
|         this.actor.add(this._separator, { expand: true }); | ||||
|     }, | ||||
|  | ||||
| @@ -395,6 +396,7 @@ const PopupImageMenuItem = new Lang.Class({ | ||||
|         this.actor.add_child(this.label); | ||||
|         this._icon = new St.Icon({ style_class: 'popup-menu-icon' }); | ||||
|         this.actor.add_child(this._icon, { align: St.Align.END }); | ||||
|         this.actor.label_actor = this.label; | ||||
|  | ||||
|         this.setIcon(iconName); | ||||
|     }, | ||||
| @@ -1058,11 +1060,6 @@ const PopupSubMenuMenuItem = new Lang.Class({ | ||||
|         let expander = new St.Bin({ style_class: 'popup-menu-item-expander' }); | ||||
|         this.actor.add(expander, { expand: true }); | ||||
|  | ||||
|         this.status = new St.Label({ style_class: 'popup-status-menu-item', | ||||
|                                      y_expand: true, | ||||
|                                      y_align: Clutter.ActorAlign.CENTER }); | ||||
|         this.actor.add_child(this.status); | ||||
|  | ||||
|         this._triangle = arrowIcon(St.Side.RIGHT); | ||||
|         this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 }); | ||||
|  | ||||
| @@ -1149,6 +1146,16 @@ const PopupSubMenuMenuItem = new Lang.Class({ | ||||
|         this.actor.remove_style_pseudo_class ('active'); | ||||
|         this._setOpenState(!this._getOpenState()); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onTouchEvent: function(actor, event) { | ||||
|         if (event.type() == Clutter.EventType.TOUCH_END) { | ||||
|             // Since we override the parent, we need to manage what the parent does | ||||
|             // with the active style class | ||||
|             this.actor.remove_style_pseudo_class ('active'); | ||||
|             this._setOpenState(!this._getOpenState()); | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -63,7 +63,7 @@ const SearchProvider2Iface = '<node> \ | ||||
| var SearchProviderProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProviderIface); | ||||
| var SearchProvider2ProxyInfo = Gio.DBusInterfaceInfo.new_for_xml(SearchProvider2Iface); | ||||
|  | ||||
| function loadRemoteSearchProviders(callback) { | ||||
| function loadRemoteSearchProviders(searchSettings, callback) { | ||||
|     let objectPaths = {}; | ||||
|     let loadedProviders = []; | ||||
|  | ||||
| @@ -124,7 +124,6 @@ function loadRemoteSearchProviders(callback) { | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     let searchSettings = new Gio.Settings({ schema_id: Search.SEARCH_PROVIDERS_SCHEMA }); | ||||
|     if (searchSettings.get_boolean('disable-external')) { | ||||
|         callback([]); | ||||
|         return; | ||||
| @@ -236,8 +235,10 @@ const RemoteSearchProvider = new Lang.Class({ | ||||
|  | ||||
|     _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))); | ||||
|             if (error.matches(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED)) | ||||
|                 return; | ||||
|  | ||||
|             log('Received error from DBus search provider %s: %s'.format(this.id, String(error))); | ||||
|             callback([]); | ||||
|             return; | ||||
|         } | ||||
|   | ||||
| @@ -61,6 +61,7 @@ const RunDialog = new Lang.Class({ | ||||
|  | ||||
|                                    // rt is short for "reload theme" | ||||
|                                    'rt': Lang.bind(this, function() { | ||||
|                                        Main.reloadThemeResource(); | ||||
|                                        Main.loadTheme(); | ||||
|                                    }) | ||||
|                                  }; | ||||
|   | ||||
| @@ -507,21 +507,22 @@ const ScreenShield = new Lang.Class({ | ||||
|                                                       this._liftShield(true, 0); | ||||
|                                               })); | ||||
|  | ||||
|         this._inhibitor = null; | ||||
|         this._aboutToSuspend = false; | ||||
|         this._loginManager = LoginManager.getLoginManager(); | ||||
|         this._loginManager.connect('prepare-for-sleep', | ||||
|                                    Lang.bind(this, this._prepareForSleep)); | ||||
|         this._inhibitSuspend(); | ||||
|  | ||||
|         this._loginSession = null; | ||||
|         this._loginManager.getCurrentSessionProxy(Lang.bind(this, | ||||
|             function(sessionProxy) { | ||||
|                 this._loginSession = sessionProxy; | ||||
|                 this._loginSession.connectSignal('Lock', Lang.bind(this, function() { this.lock(false); })); | ||||
|                 this._loginSession.connectSignal('Unlock', Lang.bind(this, function() { this.deactivate(false); })); | ||||
|                 this._loginSession.connect('g-properties-changed', Lang.bind(this, this._syncInhibitor)); | ||||
|                 this._syncInhibitor(); | ||||
|             })); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema_id: SCREENSAVER_SCHEMA }); | ||||
|         this._settings.connect('changed::' + LOCK_ENABLED_KEY, Lang.bind(this, this._syncInhibitor)); | ||||
|  | ||||
|         this._isModal = false; | ||||
|         this._hasLockScreen = false; | ||||
| @@ -547,6 +548,18 @@ const ScreenShield = new Lang.Class({ | ||||
|  | ||||
|         this.idleMonitor = Meta.IdleMonitor.get_core(); | ||||
|         this._cursorTracker = Meta.CursorTracker.get_for_screen(global.screen); | ||||
|  | ||||
|         this._syncInhibitor(); | ||||
|     }, | ||||
|  | ||||
|     _setActive: function(active) { | ||||
|         let prevIsActive = this._isActive; | ||||
|         this._isActive = active; | ||||
|  | ||||
|         if (prevIsActive != this._isActive) | ||||
|             this.emit('active-changed'); | ||||
|  | ||||
|         this._syncInhibitor(); | ||||
|     }, | ||||
|  | ||||
|     _createBackground: function(monitorIndex) { | ||||
| @@ -664,31 +677,28 @@ const ScreenShield = new Lang.Class({ | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _inhibitSuspend: function() { | ||||
|         this._loginManager.inhibit(_("GNOME needs to lock the screen"), | ||||
|                                    Lang.bind(this, function(inhibitor) { | ||||
|                                        this._inhibitor = inhibitor; | ||||
|                                    })); | ||||
|     }, | ||||
|  | ||||
|     _uninhibitSuspend: function() { | ||||
|         if (this._inhibitor) | ||||
|             this._inhibitor.close(null); | ||||
|         this._inhibitor = null; | ||||
|     _syncInhibitor: function() { | ||||
|         let inhibit = (this._loginSession && this._loginSession.Active && | ||||
|                        !this._isActive && this._settings.get_boolean(LOCK_ENABLED_KEY)); | ||||
|         if (inhibit) { | ||||
|             this._loginManager.inhibit(_("GNOME needs to lock the screen"), | ||||
|                                        Lang.bind(this, function(inhibitor) { | ||||
|                                            if (this._inhibitor) | ||||
|                                                this._inhibitor.close(null); | ||||
|                                            this._inhibitor = inhibitor; | ||||
|                                        })); | ||||
|         } else { | ||||
|             if (this._inhibitor) | ||||
|                 this._inhibitor.close(null); | ||||
|             this._inhibitor = null; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _prepareForSleep: function(loginManager, aboutToSuspend) { | ||||
|         this._aboutToSuspend = aboutToSuspend; | ||||
|  | ||||
|         if (aboutToSuspend) { | ||||
|             if (!this._settings.get_boolean(LOCK_ENABLED_KEY)) { | ||||
|                 this._uninhibitSuspend(); | ||||
|                 return; | ||||
|             } | ||||
|             this.lock(true); | ||||
|             if (this._settings.get_boolean(LOCK_ENABLED_KEY)) | ||||
|                 this.lock(true); | ||||
|         } else { | ||||
|             this._inhibitSuspend(); | ||||
|  | ||||
|             this._wakeUpScreen(); | ||||
|         } | ||||
|     }, | ||||
| @@ -1083,15 +1093,7 @@ const ScreenShield = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _completeLockScreenShown: function() { | ||||
|         let prevIsActive = this._isActive; | ||||
|         this._isActive = true; | ||||
|  | ||||
|         if (prevIsActive != this._isActive) | ||||
|             this.emit('active-changed'); | ||||
|  | ||||
|         if (this._aboutToSuspend) | ||||
|             this._uninhibitSuspend(); | ||||
|  | ||||
|         this._setActive(true); | ||||
|         this.emit('lock-screen-shown'); | ||||
|     }, | ||||
|  | ||||
| @@ -1185,8 +1187,7 @@ const ScreenShield = new Lang.Class({ | ||||
|             // gnome-settings-daemon will stop blanking the screen | ||||
|  | ||||
|             this._activationTime = 0; | ||||
|             this._isActive = false; | ||||
|             this.emit('active-changed'); | ||||
|             this._setActive(false); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -1229,9 +1230,8 @@ const ScreenShield = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         this._activationTime = 0; | ||||
|         this._isActive = false; | ||||
|         this._setActive(false); | ||||
|         this._isLocked = false; | ||||
|         this.emit('active-changed'); | ||||
|         this.emit('locked-changed'); | ||||
|         global.set_runtime_state(LOCKED_STATE_STR, null); | ||||
|     }, | ||||
|   | ||||
| @@ -283,8 +283,8 @@ const SelectArea = new Lang.Class({ | ||||
|     _getGeometry: function() { | ||||
|         return { x: Math.min(this._startX, this._lastX), | ||||
|                  y: Math.min(this._startY, this._lastY), | ||||
|                  width: Math.abs(this._startX - this._lastX), | ||||
|                  height: Math.abs(this._startY - this._lastY) }; | ||||
|                  width: Math.abs(this._startX - this._lastX) + 1, | ||||
|                  height: Math.abs(this._startY - this._lastY) + 1 }; | ||||
|     }, | ||||
|  | ||||
|     _onMotionEvent: function(actor, event) { | ||||
| @@ -292,6 +292,8 @@ const SelectArea = new Lang.Class({ | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         [this._lastX, this._lastY] = event.get_coords(); | ||||
|         this._lastX = Math.floor(this._lastX); | ||||
|         this._lastY = Math.floor(this._lastY); | ||||
|         let geometry = this._getGeometry(); | ||||
|  | ||||
|         this._rubberband.set_position(geometry.x, geometry.y); | ||||
| @@ -302,6 +304,8 @@ const SelectArea = new Lang.Class({ | ||||
|  | ||||
|     _onButtonPress: function(actor, event) { | ||||
|         [this._startX, this._startY] = event.get_coords(); | ||||
|         this._startX = Math.floor(this._startX); | ||||
|         this._startY = Math.floor(this._startY); | ||||
|         this._rubberband.set_position(this._startX, this._startY); | ||||
|  | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|   | ||||
| @@ -205,6 +205,13 @@ const SearchResultsBase = new Lang.Class({ | ||||
|                     callback(false); | ||||
|                     return; | ||||
|                 } | ||||
|                 if (metas.some(function(meta) { | ||||
|                     return !meta.name || !meta.id; | ||||
|                 })) { | ||||
|                     log('Invalid result meta returned from search provider ' + this.provider.id); | ||||
|                     callback(false); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 metasNeeded.forEach(Lang.bind(this, function(resultId, i) { | ||||
|                     let meta = metas[i]; | ||||
| @@ -409,6 +416,7 @@ const SearchResults = new Lang.Class({ | ||||
|  | ||||
|         this._searchSettings = new Gio.Settings({ schema_id: SEARCH_PROVIDERS_SCHEMA }); | ||||
|         this._searchSettings.connect('changed::disabled', Lang.bind(this, this._reloadRemoteProviders)); | ||||
|         this._searchSettings.connect('changed::enabled', 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)); | ||||
|  | ||||
| @@ -427,7 +435,7 @@ const SearchResults = new Lang.Class({ | ||||
|             this._unregisterProvider(provider); | ||||
|         })); | ||||
|  | ||||
|         RemoteSearch.loadRemoteSearchProviders(Lang.bind(this, function(providers) { | ||||
|         RemoteSearch.loadRemoteSearchProviders(this._searchSettings, Lang.bind(this, function(providers) { | ||||
|             providers.forEach(Lang.bind(this, this._registerProvider)); | ||||
|         })); | ||||
|     }, | ||||
|   | ||||
| @@ -49,9 +49,9 @@ function _setButtonsForChoices(dialog, choices) { | ||||
| function _setLabelsForMessage(dialog, message) { | ||||
|     let labels = message.split('\n'); | ||||
|  | ||||
|     _setLabelText(dialog.subjectLabel, labels[0]); | ||||
|     if (labels.length > 1) | ||||
|         _setLabelText(dialog.descriptionLabel, labels[1]); | ||||
|     _setLabelText(dialog.subjectLabel, labels.shift()); | ||||
|     if (labels.length > 0) | ||||
|         _setLabelText(dialog.descriptionLabel, labels.join('\n')); | ||||
| } | ||||
|  | ||||
| function _createIcon(gicon) { | ||||
| @@ -347,7 +347,7 @@ const ShellMountPasswordDialog = new Lang.Class({ | ||||
|         mainContentBox.add(this._messageBox, | ||||
|                            { y_align: St.Align.START, expand: true, x_fill: true, y_fill: true }); | ||||
|  | ||||
|         let subject = new St.Label({ style_class: 'prompt-dialog-headline' }); | ||||
|         let subject = new St.Label({ style_class: 'prompt-dialog-headline headline' }); | ||||
|         this._messageBox.add(subject, | ||||
|                              { y_fill:  false, | ||||
|                                y_align: St.Align.START }); | ||||
|   | ||||
| @@ -137,6 +137,10 @@ const Slider = new Lang.Class({ | ||||
|             this._motionId = this.actor.connect('motion-event', Lang.bind(this, this._motionEvent)); | ||||
|         } | ||||
|  | ||||
|         // We need to emit 'drag-begin' before moving the handle to make | ||||
|         // sure that no 'value-changed' signal is emitted before this one. | ||||
|         this.emit('drag-begin'); | ||||
|  | ||||
|         let absX, absY; | ||||
|         [absX, absY] = event.get_coords(); | ||||
|         this._moveHandle(absX, absY); | ||||
| @@ -224,6 +228,7 @@ const Slider = new Lang.Class({ | ||||
|             let delta = key == Clutter.KEY_Right ? 0.1 : -0.1; | ||||
|             this._value = Math.max(0, Math.min(this._value + delta, 1)); | ||||
|             this.actor.queue_repaint(); | ||||
|             this.emit('drag-begin'); | ||||
|             this.emit('value-changed', this._value); | ||||
|             this.emit('drag-end'); | ||||
|             return Clutter.EVENT_STOP; | ||||
|   | ||||
| @@ -119,23 +119,46 @@ const ATIndicator = new Lang.Class({ | ||||
|  | ||||
|     _buildItem: function(string, schema, key) { | ||||
|         let settings = new Gio.Settings({ schema_id: schema }); | ||||
|         settings.connect('changed::'+key, Lang.bind(this, function() { | ||||
|             widget.setToggleState(settings.get_boolean(key)); | ||||
|  | ||||
|             this._queueSyncMenuVisibility(); | ||||
|         })); | ||||
|  | ||||
|         let widget = this._buildItemExtended(string, | ||||
|             settings.get_boolean(key), | ||||
|             settings.is_writable(key), | ||||
|             function(enabled) { | ||||
|                 return settings.set_boolean(key, enabled); | ||||
|             }); | ||||
|         settings.connect('changed::'+key, Lang.bind(this, function() { | ||||
|             widget.setToggleState(settings.get_boolean(key)); | ||||
|  | ||||
|             this._queueSyncMenuVisibility(); | ||||
|         })); | ||||
|         return widget; | ||||
|     }, | ||||
|  | ||||
|     _buildHCItem: function() { | ||||
|         let interfaceSettings = new Gio.Settings({ schema_id: DESKTOP_INTERFACE_SCHEMA }); | ||||
|         let wmSettings = new Gio.Settings({ schema_id: WM_SCHEMA }); | ||||
|         interfaceSettings.connect('changed::' + KEY_GTK_THEME, Lang.bind(this, function() { | ||||
|             let value = interfaceSettings.get_string(KEY_GTK_THEME); | ||||
|             if (value == HIGH_CONTRAST_THEME) { | ||||
|                 highContrast.setToggleState(true); | ||||
|             } else { | ||||
|                 highContrast.setToggleState(false); | ||||
|                 gtkTheme = value; | ||||
|             } | ||||
|  | ||||
|             this._queueSyncMenuVisibility(); | ||||
|         })); | ||||
|         interfaceSettings.connect('changed::' + KEY_ICON_THEME, function() { | ||||
|             let value = interfaceSettings.get_string(KEY_ICON_THEME); | ||||
|             if (value != HIGH_CONTRAST_THEME) | ||||
|                 iconTheme = value; | ||||
|         }); | ||||
|         wmSettings.connect('changed::' + KEY_WM_THEME, function() { | ||||
|             let value = wmSettings.get_string(KEY_WM_THEME); | ||||
|             if (value != HIGH_CONTRAST_THEME) | ||||
|                 wmTheme = value; | ||||
|         }); | ||||
|  | ||||
|         let gtkTheme = interfaceSettings.get_string(KEY_GTK_THEME); | ||||
|         let iconTheme = interfaceSettings.get_string(KEY_ICON_THEME); | ||||
|         let wmTheme = wmSettings.get_string(KEY_WM_THEME); | ||||
| @@ -161,32 +184,18 @@ const ATIndicator = new Lang.Class({ | ||||
|                     wmSettings.reset(KEY_WM_THEME); | ||||
|                 } | ||||
|             }); | ||||
|         interfaceSettings.connect('changed::' + KEY_GTK_THEME, Lang.bind(this, function() { | ||||
|             let value = interfaceSettings.get_string(KEY_GTK_THEME); | ||||
|             if (value == HIGH_CONTRAST_THEME) { | ||||
|                 highContrast.setToggleState(true); | ||||
|             } else { | ||||
|                 highContrast.setToggleState(false); | ||||
|                 gtkTheme = value; | ||||
|             } | ||||
|  | ||||
|             this._queueSyncMenuVisibility(); | ||||
|         })); | ||||
|         interfaceSettings.connect('changed::' + KEY_ICON_THEME, function() { | ||||
|             let value = interfaceSettings.get_string(KEY_ICON_THEME); | ||||
|             if (value != HIGH_CONTRAST_THEME) | ||||
|                 iconTheme = value; | ||||
|         }); | ||||
|         wmSettings.connect('changed::' + KEY_WM_THEME, function() { | ||||
|             let value = wmSettings.get_string(KEY_WM_THEME); | ||||
|             if (value != HIGH_CONTRAST_THEME) | ||||
|                 wmTheme = value; | ||||
|         }); | ||||
|         return highContrast; | ||||
|     }, | ||||
|  | ||||
|     _buildFontItem: function() { | ||||
|         let settings = new Gio.Settings({ schema_id: DESKTOP_INTERFACE_SCHEMA }); | ||||
|         settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, Lang.bind(this, function() { | ||||
|             let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR); | ||||
|             let active = (factor > 1.0); | ||||
|             widget.setToggleState(active); | ||||
|  | ||||
|             this._queueSyncMenuVisibility(); | ||||
|         })); | ||||
|  | ||||
|         let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR); | ||||
|         let initial_setting = (factor > 1.0); | ||||
| @@ -200,13 +209,6 @@ const ATIndicator = new Lang.Class({ | ||||
|                 else | ||||
|                     settings.reset(KEY_TEXT_SCALING_FACTOR); | ||||
|             }); | ||||
|         settings.connect('changed::' + KEY_TEXT_SCALING_FACTOR, Lang.bind(this, function() { | ||||
|             let factor = settings.get_double(KEY_TEXT_SCALING_FACTOR); | ||||
|             let active = (factor > 1.0); | ||||
|             widget.setToggleState(active); | ||||
|  | ||||
|             this._queueSyncMenuVisibility(); | ||||
|         })); | ||||
|         return widget; | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -1,11 +1,8 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GLib = imports.gi.GLib; | ||||
| 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 PanelMenu = imports.ui.panelMenu; | ||||
| @@ -23,6 +20,8 @@ const RfkillManagerInterface = '<node> \ | ||||
|  | ||||
| const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface); | ||||
|  | ||||
| const HAD_BLUETOOTH_DEVICES_SETUP = 'had-bluetooth-devices-setup'; | ||||
|  | ||||
| const Indicator = new Lang.Class({ | ||||
|     Name: 'BTIndicator', | ||||
|     Extends: PanelMenu.SystemIndicator, | ||||
| @@ -32,6 +31,7 @@ const Indicator = new Lang.Class({ | ||||
|  | ||||
|         this._indicator = this._addIndicator(); | ||||
|         this._indicator.icon_name = 'bluetooth-active-symbolic'; | ||||
|         this._hadSetupDevices = global.settings.get_boolean(HAD_BLUETOOTH_DEVICES_SETUP); | ||||
|  | ||||
|         this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH, | ||||
|                                              Lang.bind(this, function(proxy, error) { | ||||
| @@ -44,13 +44,15 @@ const Indicator = new Lang.Class({ | ||||
|                                              })); | ||||
|         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._proxy.BluetoothAirplaneMode = true; | ||||
|  | ||||
|         this._toggleItem = new PopupMenu.PopupMenuItem(''); | ||||
|         this._toggleItem.connect('activate', Lang.bind(this, function() { | ||||
|             this._proxy.BluetoothAirplaneMode = !this._proxy.BluetoothAirplaneMode; | ||||
|         })); | ||||
|         this._item.menu.addMenuItem(this._toggleItem); | ||||
|  | ||||
|         this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop'); | ||||
|         this.menu.addMenuItem(this._item); | ||||
|  | ||||
| @@ -68,41 +70,75 @@ const Indicator = new Lang.Class({ | ||||
|         while (ret) { | ||||
|             let isDefault = this._model.get_value(iter, | ||||
|                                                   GnomeBluetooth.Column.DEFAULT); | ||||
|             if (isDefault) | ||||
|             let isPowered = this._model.get_value(iter, | ||||
|                                                   GnomeBluetooth.Column.POWERED); | ||||
|             if (isDefault && isPowered) | ||||
|                 return iter; | ||||
|             ret = this._model.iter_next(iter); | ||||
|         } | ||||
|         return null; | ||||
|     }, | ||||
|  | ||||
|     _getNConnectedDevices: function() { | ||||
|     // nDevices is the number of devices setup for the current default | ||||
|     // adapter if one exists and is powered. If unpowered or unavailable, | ||||
|     // nDevice is "1" if it had setup devices associated to it the last | ||||
|     // time it was seen, and "-1" if not. | ||||
|     // | ||||
|     // nConnectedDevices is the number of devices connected to the default | ||||
|     // adapter if one exists and is powered, or -1 if it's not available. | ||||
|     _getNDevices: function() { | ||||
|         let adapter = this._getDefaultAdapter(); | ||||
|         if (!adapter) | ||||
|             return 0; | ||||
|             return [ this._hadSetupDevices ? 1 : -1, -1 ]; | ||||
|  | ||||
|         let nConnectedDevices = 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) | ||||
|                 nConnectedDevices++; | ||||
|  | ||||
|             let isPaired = this._model.get_value(iter, | ||||
|                                                  GnomeBluetooth.Column.PAIRED); | ||||
|             let isTrusted = this._model.get_value(iter, | ||||
|                                                   GnomeBluetooth.Column.TRUSTED); | ||||
|             if (isPaired || isTrusted) | ||||
|                 nDevices++; | ||||
|             ret = this._model.iter_next(iter); | ||||
|         } | ||||
|         return nDevices; | ||||
|  | ||||
|         if (this._hadSetupDevices != (nDevices > 0)) { | ||||
|             this._hadSetupDevices = !this._hadSetupDevices; | ||||
|             global.settings.set_boolean(HAD_BLUETOOTH_DEVICES_SETUP, this._hadSetupDevices); | ||||
|         } | ||||
|  | ||||
|         return [ nDevices, nConnectedDevices]; | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         let nDevices = this._getNConnectedDevices(); | ||||
|         let [ nDevices, nConnectedDevices ] = this._getNDevices(); | ||||
|         let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter; | ||||
|  | ||||
|         this.menu.setSensitive(sensitive); | ||||
|         this._indicator.visible = nDevices > 0; | ||||
|         this._item.actor.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode; | ||||
|         this._indicator.visible = nConnectedDevices > 0; | ||||
|  | ||||
|         // Remember if there were setup devices and show the menu | ||||
|         // if we've seen setup devices and we're not hard blocked | ||||
|         if (nDevices > 0) | ||||
|             this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices", nDevices).format(nDevices); | ||||
|             this._item.actor.visible = !this._proxy.BluetoothHardwareAirplaneMode; | ||||
|         else | ||||
|             this._item.status.text = _("Not Connected"); | ||||
|             this._item.actor.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode; | ||||
|  | ||||
|         if (nConnectedDevices > 0) | ||||
|             /* Translators: this is the number of connected bluetooth devices */ | ||||
|             this._item.label.text = ngettext("%d Connected", "%d Connected", nConnectedDevices).format(nConnectedDevices); | ||||
|         else if (nConnectedDevices == -1) | ||||
|             this._item.label.text = _("Off"); | ||||
|         else | ||||
|             this._item.label.text = _("Not In Use"); | ||||
|  | ||||
|         this._toggleItem.label.text = this._proxy.BluetoothAirplaneMode ? _("Turn On") : _("Turn Off"); | ||||
|     }, | ||||
| }); | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| @@ -18,10 +19,6 @@ const PanelMenu = imports.ui.panelMenu; | ||||
| const SwitcherPopup = imports.ui.switcherPopup; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| const DESKTOP_INPUT_SOURCES_SCHEMA = 'org.gnome.desktop.input-sources'; | ||||
| const KEY_INPUT_SOURCES = 'sources'; | ||||
| const KEY_KEYBOARD_OPTIONS = 'xkb-options'; | ||||
|  | ||||
| const INPUT_SOURCE_TYPE_XKB = 'xkb'; | ||||
| const INPUT_SOURCE_TYPE_IBUS = 'ibus'; | ||||
|  | ||||
| @@ -142,6 +139,149 @@ const InputSourceSwitcher = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const InputSourceSettings = new Lang.Class({ | ||||
|     Name: 'InputSourceSettings', | ||||
|     Abstract: true, | ||||
|  | ||||
|     _emitInputSourcesChanged: function() { | ||||
|         this.emit('input-sources-changed'); | ||||
|     }, | ||||
|  | ||||
|     _emitKeyboardOptionsChanged: function() { | ||||
|         this.emit('keyboard-options-changed'); | ||||
|     }, | ||||
|  | ||||
|     _emitPerWindowChanged: function() { | ||||
|         this.emit('per-window-changed'); | ||||
|     }, | ||||
|  | ||||
|     get inputSources() { | ||||
|         return []; | ||||
|     }, | ||||
|  | ||||
|     get keyboardOptions() { | ||||
|         return []; | ||||
|     }, | ||||
|  | ||||
|     get perWindow() { | ||||
|         return false; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(InputSourceSettings.prototype); | ||||
|  | ||||
| const InputSourceSystemSettings = new Lang.Class({ | ||||
|     Name: 'InputSourceSystemSettings', | ||||
|     Extends: InputSourceSettings, | ||||
|  | ||||
|     _BUS_NAME: 'org.freedesktop.locale1', | ||||
|     _BUS_PATH: '/org/freedesktop/locale1', | ||||
|     _BUS_IFACE: 'org.freedesktop.locale1', | ||||
|     _BUS_PROPS_IFACE: 'org.freedesktop.DBus.Properties', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._layouts = ''; | ||||
|         this._variants = ''; | ||||
|         this._options = ''; | ||||
|  | ||||
|         this._reload(); | ||||
|  | ||||
|         Gio.DBus.system.signal_subscribe(this._BUS_NAME, | ||||
|                                          this._BUS_PROPS_IFACE, | ||||
|                                          'PropertiesChanged', | ||||
|                                          this._BUS_PATH, | ||||
|                                          null, | ||||
|                                          Gio.DBusSignalFlags.NONE, | ||||
|                                          Lang.bind(this, this._reload)); | ||||
|     }, | ||||
|  | ||||
|     _reload: function() { | ||||
|         Gio.DBus.system.call(this._BUS_NAME, | ||||
|                              this._BUS_PATH, | ||||
|                              this._BUS_PROPS_IFACE, | ||||
|                              'GetAll', | ||||
|                              new GLib.Variant('(s)', [this._BUS_IFACE]), | ||||
|                              null, Gio.DBusCallFlags.NONE, -1, null, | ||||
|                              Lang.bind(this, function(conn, result) { | ||||
|                                  let props; | ||||
|                                  try { | ||||
|                                      props = conn.call_finish(result).deep_unpack()[0]; | ||||
|                                  } catch(e) { | ||||
|                                      log('Could not get properties from ' + this._BUS_NAME); | ||||
|                                      return; | ||||
|                                  } | ||||
|                                  let layouts = props['X11Layout'].unpack(); | ||||
|                                  let variants = props['X11Variant'].unpack(); | ||||
|                                  let options = props['X11Options'].unpack(); | ||||
|  | ||||
|                                  if (layouts != this._layouts || | ||||
|                                      variants != this._variants) { | ||||
|                                      this._layouts = layouts; | ||||
|                                      this._variants = variants; | ||||
|                                      this._emitInputSourcesChanged(); | ||||
|                                  } | ||||
|                                  if (options != this._options) { | ||||
|                                      this._options = options; | ||||
|                                      this._emitKeyboardOptionsChanged(); | ||||
|                                  } | ||||
|                              })); | ||||
|     }, | ||||
|  | ||||
|     get inputSources() { | ||||
|         let sourcesList = []; | ||||
|         let layouts = this._layouts.split(','); | ||||
|         let variants = this._variants.split(','); | ||||
|  | ||||
|         for (let i = 0; i < layouts.length && !!layouts[i]; i++) { | ||||
|             let id = layouts[i]; | ||||
|             if (!!variants[i]) | ||||
|                 id += '+' + variants[i]; | ||||
|             sourcesList.push({ type: INPUT_SOURCE_TYPE_XKB, id: id }); | ||||
|         } | ||||
|         return sourcesList; | ||||
|     }, | ||||
|  | ||||
|     get keyboardOptions() { | ||||
|         return this._options.split(','); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const InputSourceSessionSettings = new Lang.Class({ | ||||
|     Name: 'InputSourceSessionSettings', | ||||
|     Extends: InputSourceSettings, | ||||
|  | ||||
|     _DESKTOP_INPUT_SOURCES_SCHEMA: 'org.gnome.desktop.input-sources', | ||||
|     _KEY_INPUT_SOURCES: 'sources', | ||||
|     _KEY_KEYBOARD_OPTIONS: 'xkb-options', | ||||
|     _KEY_PER_WINDOW: 'per-window', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._settings = new Gio.Settings({ schema_id: this._DESKTOP_INPUT_SOURCES_SCHEMA }); | ||||
|         this._settings.connect('changed::' + this._KEY_INPUT_SOURCES, Lang.bind(this, this._emitInputSourcesChanged)); | ||||
|         this._settings.connect('changed::' + this._KEY_KEYBOARD_OPTIONS, Lang.bind(this, this._emitKeyboardOptionsChanged)); | ||||
|         this._settings.connect('changed::' + this._KEY_PER_WINDOW, Lang.bind(this, this._emitPerWindowChanged)); | ||||
|     }, | ||||
|  | ||||
|     get inputSources() { | ||||
|         let sourcesList = []; | ||||
|         let sources = this._settings.get_value(this._KEY_INPUT_SOURCES); | ||||
|         let nSources = sources.n_children(); | ||||
|  | ||||
|         for (let i = 0; i < nSources; i++) { | ||||
|             let [type, id] = sources.get_child_value(i).deep_unpack(); | ||||
|             sourcesList.push({ type: type, id: id }); | ||||
|         } | ||||
|         return sourcesList; | ||||
|     }, | ||||
|  | ||||
|     get keyboardOptions() { | ||||
|         return this._settings.get_strv(this._KEY_KEYBOARD_OPTIONS); | ||||
|     }, | ||||
|  | ||||
|     get perWindow() { | ||||
|         return this._settings.get_boolean(this._KEY_PER_WINDOW); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const InputSourceManager = new Lang.Class({ | ||||
|     Name: 'InputSourceManager', | ||||
|  | ||||
| @@ -155,11 +295,11 @@ const InputSourceManager = new Lang.Class({ | ||||
|         this._ibusSources = {}; | ||||
|  | ||||
|         this._currentSource = null; | ||||
|         this._backupSource = null; | ||||
|  | ||||
|         // All valid input sources currently in the gsettings | ||||
|         // KEY_INPUT_SOURCES list ordered by most recently used | ||||
|         this._mruSources = []; | ||||
|         this._mruSourcesBackup = null; | ||||
|         this._keybindingAction = | ||||
|             Main.wm.addKeybinding('switch-input-source', | ||||
|                                   new Gio.Settings({ schema_id: "org.gnome.desktop.wm.keybindings" }), | ||||
| @@ -172,9 +312,12 @@ const InputSourceManager = new Lang.Class({ | ||||
|                                   Meta.KeyBindingFlags.IS_REVERSED, | ||||
|                                   Shell.ActionMode.ALL, | ||||
|                                   Lang.bind(this, this._switchInputSource)); | ||||
|         this._settings = new Gio.Settings({ schema_id: DESKTOP_INPUT_SOURCES_SCHEMA }); | ||||
|         this._settings.connect('changed::' + KEY_INPUT_SOURCES, Lang.bind(this, this._inputSourcesChanged)); | ||||
|         this._settings.connect('changed::' + KEY_KEYBOARD_OPTIONS, Lang.bind(this, this._keyboardOptionsChanged)); | ||||
|         if (Main.sessionMode.isGreeter) | ||||
|             this._settings = new InputSourceSystemSettings(); | ||||
|         else | ||||
|             this._settings = new InputSourceSessionSettings(); | ||||
|         this._settings.connect('input-sources-changed', Lang.bind(this, this._inputSourcesChanged)); | ||||
|         this._settings.connect('keyboard-options-changed', Lang.bind(this, this._keyboardOptionsChanged)); | ||||
|  | ||||
|         this._xkbInfo = KeyboardManager.getXkbInfo(); | ||||
|         this._keyboardManager = KeyboardManager.getKeyboardManager(); | ||||
| @@ -192,13 +335,13 @@ const InputSourceManager = new Lang.Class({ | ||||
|         this._focusWindowNotifyId = 0; | ||||
|         this._overviewShowingId = 0; | ||||
|         this._overviewHiddenId = 0; | ||||
|         this._settings.connect('changed::per-window', Lang.bind(this, this._sourcesPerWindowChanged)); | ||||
|         this._settings.connect('per-window-changed', Lang.bind(this, this._sourcesPerWindowChanged)); | ||||
|         this._sourcesPerWindowChanged(); | ||||
|         this._disableIBus = false; | ||||
|     }, | ||||
|  | ||||
|     reload: function() { | ||||
|         this._keyboardManager.setKeyboardOptions(this._settings.get_strv(KEY_KEYBOARD_OPTIONS)); | ||||
|         this._keyboardManager.setKeyboardOptions(this._settings.keyboardOptions); | ||||
|         this._inputSourcesChanged(); | ||||
|     }, | ||||
|  | ||||
| @@ -253,7 +396,7 @@ const InputSourceManager = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _keyboardOptionsChanged: function() { | ||||
|         this._keyboardManager.setKeyboardOptions(this._settings.get_strv(KEY_KEYBOARD_OPTIONS)); | ||||
|         this._keyboardManager.setKeyboardOptions(this._settings.keyboardOptions); | ||||
|         this._keyboardManager.reapply(); | ||||
|     }, | ||||
|  | ||||
| @@ -294,8 +437,8 @@ const InputSourceManager = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _inputSourcesChanged: function() { | ||||
|         let sources = this._settings.get_value(KEY_INPUT_SOURCES); | ||||
|         let nSources = sources.n_children(); | ||||
|         let sources = this._settings.inputSources; | ||||
|         let nSources = sources.length; | ||||
|  | ||||
|         this._inputSources = {}; | ||||
|         this._ibusSources = {}; | ||||
| @@ -304,7 +447,8 @@ const InputSourceManager = new Lang.Class({ | ||||
|         for (let i = 0; i < nSources; i++) { | ||||
|             let displayName; | ||||
|             let shortName; | ||||
|             let [type, id] = sources.get_child_value(i).deep_unpack(); | ||||
|             let type = sources[i].type; | ||||
|             let id = sources[i].id; | ||||
|             let exists = false; | ||||
|  | ||||
|             if (type == INPUT_SOURCE_TYPE_XKB) { | ||||
| @@ -372,6 +516,11 @@ const InputSourceManager = new Lang.Class({ | ||||
|  | ||||
|         this._keyboardManager.setUserLayouts(sourcesList.map(function(x) { return x.xkbId; })); | ||||
|  | ||||
|         if (!this._disableIBus && this._mruSourcesBackup) { | ||||
|             this._mruSources = this._mruSourcesBackup; | ||||
|             this._mruSourcesBackup = null; | ||||
|         } | ||||
|  | ||||
|         let mruSources = []; | ||||
|         for (let i = 0; i < this._mruSources.length; i++) { | ||||
|             for (let j = 0; j < sourcesList.length; j++) | ||||
| @@ -383,20 +532,8 @@ const InputSourceManager = new Lang.Class({ | ||||
|         } | ||||
|         this._mruSources = mruSources.concat(sourcesList); | ||||
|  | ||||
|         if (this._mruSources.length > 0) { | ||||
|             if (!this._disableIBus && this._backupSource) { | ||||
|                 for (let i = 0; i < this._mruSources.length; i++) { | ||||
|                     if (this._mruSources[i].type == this._backupSource.type && | ||||
|                         this._mruSources[i].id == this._backupSource.id) { | ||||
|                         let currentSource = this._mruSources.splice(i, 1); | ||||
|                         this._mruSources = currentSource.concat(this._mruSources); | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|                 this._backupSource = null; | ||||
|             } | ||||
|         if (this._mruSources.length > 0) | ||||
|             this._mruSources[0].activate(); | ||||
|         } | ||||
|  | ||||
|         // All ibus engines are preloaded here to reduce the launching time | ||||
|         // when users switch the input sources. | ||||
| @@ -461,27 +598,30 @@ const InputSourceManager = new Lang.Class({ | ||||
|             if (this._disableIBus) | ||||
|                 return; | ||||
|             this._disableIBus = true; | ||||
|             this._backupSource = this._currentSource; | ||||
|             this._mruSourcesBackup = this._mruSources.slice(); | ||||
|         } else { | ||||
|             if (!this._disableIBus) | ||||
|                 return; | ||||
|             this._disableIBus = false; | ||||
|         } | ||||
|         // If this._mruSources is not cleared before this.reload() is called, | ||||
|         // the order is different from the original one as IM sources will | ||||
|         // be appended to XKB sources. | ||||
|         this._mruSources = []; | ||||
|         this.reload(); | ||||
|     }, | ||||
|  | ||||
|     _getNewInputSource: function(current) { | ||||
|         for (let i in this._inputSources) { | ||||
|             let is = this._inputSources[i]; | ||||
|             if (is.type == current.type && | ||||
|                 is.id == current.id) | ||||
|                 return is; | ||||
|         let sourceIndexes = Object.keys(this._inputSources); | ||||
|         if (sourceIndexes.length == 0) | ||||
|             return null; | ||||
|  | ||||
|         if (current) { | ||||
|             for (let i in this._inputSources) { | ||||
|                 let is = this._inputSources[i]; | ||||
|                 if (is.type == current.type && | ||||
|                     is.id == current.id) | ||||
|                     return is; | ||||
|             } | ||||
|         } | ||||
|         return this._currentSource; | ||||
|  | ||||
|         return this._inputSources[sourceIndexes[0]]; | ||||
|     }, | ||||
|  | ||||
|     _getCurrentWindow: function() { | ||||
| @@ -496,20 +636,17 @@ const InputSourceManager = new Lang.Class({ | ||||
|         if (!window) | ||||
|             return; | ||||
|  | ||||
|         if (!window._inputSources) { | ||||
|             window._inputSources = this._inputSources; | ||||
|             window._currentSource = this._currentSource; | ||||
|         } else if (window._inputSources == this._inputSources) { | ||||
|             window._currentSource.activate(); | ||||
|         } else { | ||||
|         if (window._inputSources != this._inputSources) { | ||||
|             window._inputSources = this._inputSources; | ||||
|             window._currentSource = this._getNewInputSource(window._currentSource); | ||||
|             window._currentSource.activate(); | ||||
|         } | ||||
|  | ||||
|         if (window._currentSource) | ||||
|             window._currentSource.activate(); | ||||
|     }, | ||||
|  | ||||
|     _sourcesPerWindowChanged: function() { | ||||
|         this._sourcesPerWindow = this._settings.get_boolean('per-window'); | ||||
|         this._sourcesPerWindow = this._settings.perWindow; | ||||
|  | ||||
|         if (this._sourcesPerWindow && this._focusWindowNotifyId == 0) { | ||||
|             this._focusWindowNotifyId = global.display.connect('notify::focus-window', | ||||
|   | ||||
| @@ -7,12 +7,17 @@ const Lang = imports.lang; | ||||
| const Main = imports.ui.main; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const LOCATION_SCHEMA = 'org.gnome.system.location'; | ||||
| const MAX_ACCURACY_LEVEL = 'max-accuracy-level'; | ||||
| const ENABLED = 'enabled'; | ||||
|  | ||||
| const APP_PERMISSIONS_TABLE = 'desktop'; | ||||
| const APP_PERMISSIONS_ID = 'geolocation'; | ||||
|  | ||||
| const GeoclueAccuracyLevel = { | ||||
|     NONE: 0, | ||||
|     COUNTRY: 1, | ||||
| @@ -46,6 +51,26 @@ var AgentIface = '<node> \ | ||||
|   </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| var XdgAppIface = '<node> \ | ||||
|   <interface name="org.freedesktop.XdgApp.PermissionStore"> \ | ||||
|     <method name="Lookup"> \ | ||||
|       <arg name="table" type="s" direction="in"/> \ | ||||
|       <arg name="id" type="s" direction="in"/> \ | ||||
|       <arg name="permissions" type="a{sas}" direction="out"/> \ | ||||
|       <arg name="data" type="v" direction="out"/> \ | ||||
|     </method> \ | ||||
|     <method name="Set"> \ | ||||
|       <arg name="table" type="s" direction="in"/> \ | ||||
|       <arg name="create" type="b" direction="in"/> \ | ||||
|       <arg name="id" type="s" direction="in"/> \ | ||||
|       <arg name="app_permissions" type="a{sas}" direction="in"/> \ | ||||
|       <arg name="data" type="v" direction="in"/> \ | ||||
|     </method> \ | ||||
|   </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const PermissionStore = Gio.DBusProxy.makeProxyWrapper(XdgAppIface); | ||||
|  | ||||
| const Indicator = new Lang.Class({ | ||||
|     Name: 'LocationIndicator', | ||||
|     Extends: PanelMenu.SystemIndicator, | ||||
| @@ -62,13 +87,13 @@ const Indicator = new Lang.Class({ | ||||
|         this._indicator = this._addIndicator(); | ||||
|         this._indicator.icon_name = 'find-location-symbolic'; | ||||
|  | ||||
|         this._item = new PopupMenu.PopupSubMenuMenuItem(_("Location"), true); | ||||
|         this._item = new PopupMenu.PopupSubMenuMenuItem('', 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._item.label.text = _("Location Enabled"); | ||||
|         this._onOffAction = this._item.menu.addAction(_("Disable"), Lang.bind(this, this._onOnOffAction)); | ||||
|         this._item.menu.addSettingsAction(_("Privacy Settings"), 'gnome-privacy-panel.desktop'); | ||||
|  | ||||
| @@ -83,64 +108,200 @@ const Indicator = new Lang.Class({ | ||||
|         this._onSessionUpdated(); | ||||
|         this._onMaxAccuracyLevelChanged(); | ||||
|         this._connectToGeoclue(); | ||||
|         this._connectToPermissionStore(); | ||||
|     }, | ||||
|  | ||||
|     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) { | ||||
|     AuthorizeAppAsync: function(params, invocation) { | ||||
|         let [desktop_id, reqAccuracyLevel] = params; | ||||
|         log("%s is requesting location".format(desktop_id)); | ||||
|  | ||||
|         let callback = function(level, timesAllowed) { | ||||
|             if (level >= GeoclueAccuracyLevel.NONE && timesAllowed > 2) { | ||||
|                 log("%s is in store".format(desktop_id)); | ||||
|                 let accuracyLevel = clamp(reqAccuracyLevel, 0, level); | ||||
|                 this._completeAuthorizeApp(desktop_id, | ||||
|                                            accuracyLevel, | ||||
|                                            timesAllowed, | ||||
|                                            invocation); | ||||
|             } else { | ||||
|                 log("%s not in store".format(desktop_id)); | ||||
|                 this._userAuthorizeApp(desktop_id, | ||||
|                                        reqAccuracyLevel, | ||||
|                                        timesAllowed, | ||||
|                                        invocation); | ||||
|             } | ||||
|         } | ||||
|         this._fetchPermissionFromStore(desktop_id, Lang.bind(this, callback); | ||||
|     }, | ||||
|  | ||||
|     _userAuthorizeApp: function(desktopId, reqAccuracyLevel, timesAllowed, invocation) { | ||||
|         var appSystem = Shell.AppSystem.get_default(); | ||||
|         var app = appSystem.lookup_app(desktop_id + ".desktop"); | ||||
|         var app = appSystem.lookup_app(desktopId + ".desktop"); | ||||
|         if (app == null) { | ||||
|             return [false, 0]; | ||||
|             this._completeAuthorizeApp(desktopId, | ||||
|                                        GeoclueAccuracyLevel.NONE, | ||||
|                                        timesAllowed, | ||||
|                                        invocation); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let allowedAccuracyLevel = clamp(reqAccuracyLevel, 0, this._getMaxAccuracyLevel()); | ||||
|         return [true, allowedAccuracyLevel]; | ||||
|         var name = app.get_name(); | ||||
|         var icon = app.get_app_info().get_icon(); | ||||
|         var reason = app.get_string("X-Geoclue-Reason"); | ||||
|         var allowCallback = function() { | ||||
|             this._completeAuthorizeApp(desktopId, | ||||
|                                        reqAccuracyLevel, | ||||
|                                        timesAllowed, | ||||
|                                        invocation); | ||||
|         }; | ||||
|         var denyCallback = function() { | ||||
|             this._completeAuthorizeApp(desktopId, | ||||
|                                        GeoclueAccuracyLevel.NONE, | ||||
|                                        timesAllowed, | ||||
|                                        invocation); | ||||
|         }; | ||||
|  | ||||
|         this._showAppAuthDialog(name, | ||||
|                                 reason, | ||||
|                                 icon, | ||||
|                                 Lang.bind(this, allowCallback), | ||||
|                                 Lang.bind(this, denyCallback)); | ||||
|     }, | ||||
|  | ||||
|     _showAppAuthDialog: function(name, reason, icon, allowCallback, denyCallback) { | ||||
|         if (this._dialog == null) | ||||
|             this._dialog = new GeolocationDialog(name, reason, icon); | ||||
|         else | ||||
|             this._dialog.update(name, reason, icon); | ||||
|  | ||||
|         let closedId = this._dialog.connect('closed', function() { | ||||
|             this._dialog.disconnect(closedId); | ||||
|             if (this._dialog.allowed) | ||||
|                 allowCallback (); | ||||
|             else | ||||
|                 denyCallback (); | ||||
|         }.bind(this)); | ||||
|  | ||||
|         this._dialog.open(global.get_current_time ()); | ||||
|     }, | ||||
|  | ||||
|     _completeAuthorizeApp: function(desktopId, | ||||
|                                     accuracyLevel, | ||||
|                                     timesAllowed, | ||||
|                                     invocation) { | ||||
|         if (accuracyLevel == GeoclueAccuracyLevel.NONE) { | ||||
|             invocation.return_value(GLib.Variant.new('(bu)', | ||||
|                                                      [false, accuracyLevel])); | ||||
|             this._saveToPermissionStore(desktopId, accuracyLevel, 0); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let allowedAccuracyLevel = clamp(accuracyLevel, | ||||
|                                          0, | ||||
|                                          this._getMaxAccuracyLevel()); | ||||
|         invocation.return_value(GLib.Variant.new('(bu)', | ||||
|                                                  [true, allowedAccuracyLevel])); | ||||
|  | ||||
|         this._saveToPermissionStore(desktopId, allowedAccuracyLevel, timesAllowed + 1); | ||||
|     }, | ||||
|  | ||||
|     _fetchPermissionFromStore: function(desktopId, callback) { | ||||
|         if (this._permStoreProxy == null) { | ||||
|             callback (-1, 0); | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._permStoreProxy.LookupRemote(APP_PERMISSIONS_TABLE, | ||||
|                                           APP_PERMISSIONS_ID, | ||||
|                                           function(result, error) { | ||||
|             if (error != null) { | ||||
|                 log(error.message); | ||||
|                 callback(-1, 0); | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             let [permissions, data] = result; | ||||
|             let permission = permissions[desktopId]; | ||||
|             if (permission == null) { | ||||
|                 callback(-1, 0); | ||||
|  | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             let levelStr = permission[0]; | ||||
|             let level = GeoclueAccuracyLevel[levelStr.toUpperCase()] || | ||||
|                         GeoclueAccuracyLevel.NONE; | ||||
|             let timesAllowed = data.get_byte(); | ||||
|  | ||||
|             callback(level, timesAllowed); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     _saveToPermissionStore: function(desktopId, | ||||
|                                      allowedAccuracyLevel, | ||||
|                                      timesAllowed) { | ||||
|         if (timesAllowed > 2 || this._permStoreProxy == null) | ||||
|             return; | ||||
|  | ||||
|         let levelStr = Object.keys(GeoclueAccuracyLevel)[allowedAccuracyLevel];  | ||||
|         let permission = { desktopId: [levelStr] }; | ||||
|         let permissions = GLib.Variant.new('a{sas}', [permission])); | ||||
|         let data = GLib.Variant.new('y', timesAllowed); | ||||
|  | ||||
|         this._permStoreProxy.SetRemote(APP_PERMISSIONS_TABLE, | ||||
|                                        true, | ||||
|                                        APP_PERMISSIONS_ID, | ||||
|                                        permissions, | ||||
|                                        data, | ||||
|                                        function (result, error) { | ||||
|             log(error.message); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     _syncIndicator: function() { | ||||
|         if (this._proxy == null) { | ||||
|         if (this._managerProxy == null) { | ||||
|             this._indicator.visible = false; | ||||
|             this._item.actor.visible = false; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._indicator.visible = this._proxy.InUse; | ||||
|         this._indicator.visible = this._managerProxy.InUse; | ||||
|         this._item.actor.visible = this._indicator.visible; | ||||
|         this._updateMenuLabels(); | ||||
|     }, | ||||
|  | ||||
|     _connectToGeoclue: function() { | ||||
|         if (this._proxy != null || this._connecting) | ||||
|         if (this._managerProxy != 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)); | ||||
|                            Lang.bind(this, this._onManagerProxyReady)); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _onProxyReady: function(proxy, error) { | ||||
|     _onManagerProxyReady: function(proxy, error) { | ||||
|         if (error != null) { | ||||
|             log(error.message); | ||||
|             this._connecting = false; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._proxy = proxy; | ||||
|         this._propertiesChangedId = this._proxy.connect('g-properties-changed', | ||||
|         this._managerProxy = proxy; | ||||
|         this._propertiesChangedId = this._managerProxy.connect('g-properties-changed', | ||||
|                                                         Lang.bind(this, this._onGeocluePropsChanged)); | ||||
|  | ||||
|         this._syncIndicator(); | ||||
|  | ||||
|         this._proxy.AddAgentRemote('gnome-shell', Lang.bind(this, this._onAgentRegistered)); | ||||
|         this._managerProxy.AddAgentRemote('gnome-shell', Lang.bind(this, this._onAgentRegistered)); | ||||
|     }, | ||||
|  | ||||
|     _onAgentRegistered: function(result, error) { | ||||
| @@ -153,10 +314,10 @@ const Indicator = new Lang.Class({ | ||||
|  | ||||
|     _onGeoclueVanished: function() { | ||||
|         if (this._propertiesChangedId) { | ||||
|             this._proxy.disconnect(this._propertiesChangedId); | ||||
|             this._managerProxy.disconnect(this._propertiesChangedId); | ||||
|             this._propertiesChangedId = 0; | ||||
|         } | ||||
|         this._proxy = null; | ||||
|         this._managerProxy = null; | ||||
|  | ||||
|         this._syncIndicator(); | ||||
|     }, | ||||
| @@ -173,10 +334,11 @@ const Indicator = new Lang.Class({ | ||||
|  | ||||
|     _updateMenuLabels: function() { | ||||
|         if (this._settings.get_boolean(ENABLED)) { | ||||
|             this._item.status.text = this._indicator.visible ? _("In Use") : _("Enabled"); | ||||
|             this._item.label.text = this._indicator.visible ? _("Location In Use") | ||||
|                                                             : _("Location Enabled"); | ||||
|             this._onOffAction.label.text = _("Disable"); | ||||
|         } else { | ||||
|             this._item.status.text = _("Disabled"); | ||||
|             this._item.label.text = _("Location Disabled"); | ||||
|             this._onOffAction.label.text = _("Enable"); | ||||
|         } | ||||
|     }, | ||||
| @@ -210,9 +372,85 @@ const Indicator = new Lang.Class({ | ||||
|         let unpacked = properties.deep_unpack(); | ||||
|         if ("InUse" in unpacked) | ||||
|             this._syncIndicator(); | ||||
|     } | ||||
|     }, | ||||
|  | ||||
|     _connectToPermissionStore: function() { | ||||
|         this._permStoreProxy = null; | ||||
|         new PermissionStore(Gio.DBus.session, | ||||
|                            'org.freedesktop.XdgApp', | ||||
|                            '/org/freedesktop/XdgApp/PermissionStore', | ||||
|                            Lang.bind(this, this._onPermStoreProxyReady)); | ||||
|     }, | ||||
|  | ||||
|     _onPermStoreProxyReady: function(proxy, error) { | ||||
|         if (error != null) { | ||||
|             log(error.message); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._permStoreProxy = proxy; | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| function clamp(value, min, max) { | ||||
|     return Math.max(min, Math.min(max, value)); | ||||
| } | ||||
|  | ||||
| const GeolocationDialog = new Lang.Class({ | ||||
|     Name: 'GeolocationDialog', | ||||
|     Extends: ModalDialog.ModalDialog, | ||||
|  | ||||
|     // FIXME: Would be nice to show the application icon too | ||||
|     _init: function(name, reason, icon) { | ||||
|         this.parent({ destroyOnClose: false }); | ||||
|  | ||||
|         let text = _("'%s' is requesting access to location data.").format (name); | ||||
|         this._label = new St.Label({ style_class: 'prompt-dialog-description', | ||||
|                                      text: text }); | ||||
|  | ||||
|         this.contentLayout.add(this._label, {}); | ||||
|  | ||||
|         if (reason != null) { | ||||
|             this._reasonLabel = new St.Label({ style_class: 'prompt-dialog-description', | ||||
|                                                text: reason }); | ||||
|             this.contentLayout.add(this._reasonLabel, {}); | ||||
|         } else { | ||||
|             this._reasonLabel = null; | ||||
|         } | ||||
|  | ||||
|         this._allowButton = this.addButton({ label: _("Confirm"), | ||||
|                                              action: this._onAllowClicked.bind(this), | ||||
|                                              default: false }, | ||||
|                                            { expand: true, x_fill: false, x_align: St.Align.END }); | ||||
|         this._denyButton = this.addButton({ label: _("Cancel"), | ||||
|                                             action: this._onDisallowClicked.bind(this), | ||||
|                                             default: true }, | ||||
|                                           { expand: true, x_fill: false, x_align: St.Align.START }); | ||||
|     }, | ||||
|  | ||||
|     update: function(name, reason, icon) { | ||||
|         let text = _("'%s' is requesting access to location data.").format (name); | ||||
|         this._label.text = text; | ||||
|  | ||||
|         if (this._reasonLabel != null) { | ||||
|             this.contentLayout.remove(this._reasonLabel, {}); | ||||
|             this._reasonLabel = null; | ||||
|         } | ||||
|  | ||||
|         if (reason != null) { | ||||
|             this._reasonLabel = new St.Label({ style_class: 'prompt-dialog-description', | ||||
|                                                text: reason }); | ||||
|             this.contentLayout.add(this._reasonLabel, {}); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onAllowClicked: function() { | ||||
|         this.allowed = true; | ||||
|         this.close(); | ||||
|     }, | ||||
|  | ||||
|     _onDisallowClicked: function() { | ||||
|         this.allowed = false; | ||||
|         this.close(); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -257,16 +257,8 @@ const NMConnectionSection = new Lang.Class({ | ||||
|         this._radioSection.actor.visible = (nItems > 1); | ||||
|         this._labelSection.actor.visible = (nItems == 1); | ||||
|  | ||||
|         this.item.status.text = this._getStatus(); | ||||
|         this.item.label.text = this._getStatus(); | ||||
|         this.item.icon.icon_name = this._getMenuIcon(); | ||||
|  | ||||
|         // desc can be undefined at cold-plug, before we called | ||||
|         // NMGtk.disambiguate_device_names() at least once | ||||
|         let desc = this._getDescription(); | ||||
|         if (desc) | ||||
|             this.item.label.text = desc; | ||||
|         else | ||||
|             this.item.label.text = ''; | ||||
|     }, | ||||
|  | ||||
|     _getMenuIcon: function() { | ||||
| @@ -355,6 +347,7 @@ const NMConnectionDevice = new Lang.Class({ | ||||
|         this.parent(client); | ||||
|         this._device = device; | ||||
|         this._settings = settings; | ||||
|         this._description = ''; | ||||
|  | ||||
|         this._autoConnectItem = this.item.menu.addAction(_("Connect"), Lang.bind(this, this._autoConnect)); | ||||
|         this._deactivateItem = this._radioSection.addAction(_("Turn Off"), Lang.bind(this, this.deactivateConnection)); | ||||
| @@ -454,38 +447,44 @@ const NMConnectionDevice = new Lang.Class({ | ||||
|  | ||||
|         switch(this._device.state) { | ||||
|         case NetworkManager.DeviceState.DISCONNECTED: | ||||
|             return _("Off"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Off").format(this._getDescription()); | ||||
|         case NetworkManager.DeviceState.ACTIVATED: | ||||
|             return _("Connected"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Connected").format(this._getDescription()); | ||||
|         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) */ | ||||
|             return _("Unmanaged"); | ||||
|                under NetworkManager's control (and thus cannot be used in the menu); | ||||
|                %s is a network identifier */ | ||||
|             return _("%s Unmanaged").format(this._getDescription()); | ||||
|         case NetworkManager.DeviceState.DEACTIVATING: | ||||
|             return _("Disconnecting"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Disconnecting").format(this._getDescription()); | ||||
|         case NetworkManager.DeviceState.PREPARE: | ||||
|         case NetworkManager.DeviceState.CONFIG: | ||||
|         case NetworkManager.DeviceState.IP_CONFIG: | ||||
|         case NetworkManager.DeviceState.IP_CHECK: | ||||
|         case NetworkManager.DeviceState.SECONDARIES: | ||||
|             return _("Connecting"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Connecting").format(this._getDescription()); | ||||
|         case NetworkManager.DeviceState.NEED_AUTH: | ||||
|             /* Translators: this is for network connections that require some kind of key or password */ | ||||
|             return _("Authentication required"); | ||||
|             /* Translators: this is for network connections that require some kind of key or password; %s is a network identifier */ | ||||
|             return _("%s Requires Authentication").format(this._getDescription()); | ||||
|         case NetworkManager.DeviceState.UNAVAILABLE: | ||||
|             // This state is actually a compound of various states (generically unavailable, | ||||
|             // firmware missing), that are exposed by different properties (whose state may | ||||
|             // or may not updated when we receive state-changed). | ||||
|             if (this._device.firmware_missing) { | ||||
|                 /* Translators: this is for devices that require some kind of firmware or kernel | ||||
|                    module, which is missing */ | ||||
|                 return _("Firmware missing"); | ||||
|                    module, which is missing; %s is a network identifier */ | ||||
|                 return _("Firmware Missing For %s").format(this._getDescription()); | ||||
|             } | ||||
|             /* Translators: this is for a network device that cannot be activated (for example it | ||||
|                is disabled by rfkill, or it has no coverage */ | ||||
|             return _("Unavailable"); | ||||
|                is disabled by rfkill, or it has no coverage; %s is a network identifier */ | ||||
|             return _("%s Unavailable").format(this._getDescription()); | ||||
|         case NetworkManager.DeviceState.FAILED: | ||||
|             return _("Connection failed"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Connection Failed").format(this._getDescription()); | ||||
|         default: | ||||
|             log('Device state invalid, is %d'.format(this._device.state)); | ||||
|             return 'invalid'; | ||||
| @@ -585,11 +584,12 @@ const NMDeviceModem = new Lang.Class({ | ||||
|  | ||||
|     _getStatus: function() { | ||||
|         if (!this._client.wwan_hardware_enabled) | ||||
|             return _("Hardware Disabled"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Hardware Disabled").format(this._getDescription()); | ||||
|         else if (!this._client.wwan_enabled) | ||||
|             /* Translators: this is for a network device that cannot be activated | ||||
|                because it's disabled by rfkill (airplane mode) */ | ||||
|             return _("Disabled"); | ||||
|                because it's disabled by rfkill (airplane mode); %s is a network identifier */ | ||||
|             return _("%s Disabled").format(this._getDescription()); | ||||
|         else if (this._device.state == NetworkManager.DeviceState.ACTIVATED && | ||||
|                  this._mobileDevice && this._mobileDevice.operator_name) | ||||
|             return this._mobileDevice.operator_name; | ||||
| @@ -877,7 +877,7 @@ const NMWirelessDialog = new Lang.Class({ | ||||
|                                                  y_align: Clutter.ActorAlign.CENTER }); | ||||
|  | ||||
|         let file = Gio.File.new_for_uri('resource:///org/gnome/shell/theme/process-working.svg'); | ||||
|         this._noNetworksSpinner = new Animation.AnimatedIcon(file, 24, 24); | ||||
|         this._noNetworksSpinner = new Animation.AnimatedIcon(file, 16, 16); | ||||
|         this._noNetworksBox.add_actor(this._noNetworksSpinner.actor); | ||||
|         this._noNetworksBox.add_actor(new St.Label({ style_class: 'no-networks-label', | ||||
|                                                      text: _("No Networks") })); | ||||
| @@ -917,10 +917,7 @@ const NMWirelessDialog = new Lang.Class({ | ||||
|                                                   key: Clutter.Escape }); | ||||
|         this._connectButton = this.addButton({ action: Lang.bind(this, this._connect), | ||||
|                                                label: _("Connect"), | ||||
|                                                key: Clutter.Return }, | ||||
|                                              { expand: true, | ||||
|                                                x_fill: false, | ||||
|                                                x_align: St.Align.END }); | ||||
|                                                key: Clutter.Return }); | ||||
|     }, | ||||
|  | ||||
|     _connect: function() { | ||||
| @@ -1282,9 +1279,8 @@ const NMDeviceWireless = new Lang.Class({ | ||||
|         this._toggleItem.label.text = this._client.wireless_enabled ? _("Turn Off") : _("Turn On"); | ||||
|         this._toggleItem.actor.visible = this._client.wireless_hardware_enabled; | ||||
|  | ||||
|         this.item.status.text = this._getStatus(); | ||||
|         this.item.icon.icon_name = this._getMenuIcon(); | ||||
|         this.item.label.text = this._description; | ||||
|         this.item.label.text = this._getStatus(); | ||||
|     }, | ||||
|  | ||||
|     setDeviceDescription: function(desc) { | ||||
| @@ -1296,18 +1292,23 @@ const NMDeviceWireless = new Lang.Class({ | ||||
|         let ap = this._device.active_access_point; | ||||
|  | ||||
|         if (this._isHotSpotMaster()) | ||||
|             return _("Hotspot Active"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Hotspot Active").format(this._description); | ||||
|         else if (this._device.state >= NetworkManager.DeviceState.PREPARE && | ||||
|                  this._device.state < NetworkManager.DeviceState.ACTIVATED) | ||||
|             return _("Connecting"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Connecting").format(this._description); | ||||
|         else if (ap) | ||||
|             return ssidToLabel(ap.get_ssid()); | ||||
|         else if (!this._client.wireless_hardware_enabled) | ||||
|             return _("Hardware Disabled"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Hardware Disabled").format(this._description); | ||||
|         else if (!this._client.wireless_enabled) | ||||
|             return _("Off"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Off").format(this._description); | ||||
|         else if (this._device.state == NetworkManager.DeviceState.DISCONNECTED) | ||||
|             return _("Not Connected"); | ||||
|             /* Translators: %s is a network identifier */ | ||||
|             return _("%s Not Connected").format(this._description); | ||||
|         else | ||||
|             return ''; | ||||
|     }, | ||||
| @@ -1509,7 +1510,7 @@ const NMVPNSection = new Lang.Class({ | ||||
|                 return item.getName(); | ||||
|         } | ||||
|  | ||||
|         return _("Off"); | ||||
|         return _("VPN Off"); | ||||
|     }, | ||||
|  | ||||
|     _getMenuIcon: function() { | ||||
|   | ||||
| @@ -1,6 +1,8 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Gio = imports.gi.Gio; | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const St = imports.gi.St; | ||||
| const Lang = imports.lang; | ||||
| const UPower = imports.gi.UPowerGlib; | ||||
|  | ||||
| @@ -25,6 +27,8 @@ const DisplayDeviceInterface = '<node> \ | ||||
|  | ||||
| const PowerManagerProxy = Gio.DBusProxy.makeProxyWrapper(DisplayDeviceInterface); | ||||
|  | ||||
| const SHOW_BATTERY_PERCENTAGE       = 'show-battery-percentage'; | ||||
|  | ||||
| const Indicator = new Lang.Class({ | ||||
|     Name: 'PowerIndicator', | ||||
|     Extends: PanelMenu.SystemIndicator, | ||||
| @@ -32,7 +36,15 @@ const Indicator = new Lang.Class({ | ||||
|     _init: function() { | ||||
|         this.parent(); | ||||
|  | ||||
|         this._desktopSettings = new Gio.Settings({ schema_id: 'org.gnome.desktop.interface' }); | ||||
|         this._desktopSettings.connect('changed::' + SHOW_BATTERY_PERCENTAGE, | ||||
|                                       Lang.bind(this, this._sync)); | ||||
|  | ||||
|         this._indicator = this._addIndicator(); | ||||
|         this._percentageLabel = new St.Label({ y_expand: true, | ||||
|                                                y_align: Clutter.ActorAlign.CENTER }); | ||||
|         this.indicators.add(this._percentageLabel, { expand: true, y_fill: true }); | ||||
|         this.indicators.add_style_class_name('power-status'); | ||||
|  | ||||
|         this._proxy = new PowerManagerProxy(Gio.DBus.system, BUS_NAME, OBJECT_PATH, | ||||
|                                             Lang.bind(this, function(proxy, error) { | ||||
| @@ -83,12 +95,12 @@ const Indicator = new Lang.Class({ | ||||
|  | ||||
|         if (this._proxy.State == UPower.DeviceState.DISCHARGING) { | ||||
|             // Translators: this is <hours>:<minutes> Remaining (<percentage>) | ||||
|             return _("%d\u2236%02d Remaining (%d%%)").format(hours, minutes, this._proxy.Percentage); | ||||
|             return _("%d\u2236%02d Remaining (%d\u2009%%)").format(hours, minutes, this._proxy.Percentage); | ||||
|         } | ||||
|  | ||||
|         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, this._proxy.Percentage); | ||||
|             return _("%d\u2236%02d Until Full (%d\u2009%%)").format(hours, minutes, this._proxy.Percentage); | ||||
|         } | ||||
|  | ||||
|         return null; | ||||
| @@ -99,10 +111,12 @@ const Indicator = new Lang.Class({ | ||||
|         let visible = this._proxy.IsPresent; | ||||
|         if (visible) { | ||||
|             this._item.actor.show(); | ||||
|             this._percentageLabel.visible = this._desktopSettings.get_boolean(SHOW_BATTERY_PERCENTAGE); | ||||
|         } else { | ||||
|             // If there's no battery, then we use the power icon. | ||||
|             this._item.actor.hide(); | ||||
|             this._indicator.icon_name = 'system-shutdown-symbolic'; | ||||
|             this._percentageLabel.hide(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
| @@ -111,13 +125,15 @@ const Indicator = new Lang.Class({ | ||||
|         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"); | ||||
|         // The icon label | ||||
|         let label | ||||
|         if (this._proxy.State == UPower.DeviceState.FULLY_CHARGED) | ||||
|           label = _("%d\u2009%%").format(100); | ||||
|         else | ||||
|             this._item.label.text = _("Battery"); | ||||
|           label = _("%d\u2009%%").format(this._proxy.Percentage); | ||||
|         this._percentageLabel.clutter_text.set_markup('<span size="smaller">' + label + '</span>'); | ||||
|  | ||||
|         // The status label | ||||
|         this._item.label.text = this._getStatus(); | ||||
|     }, | ||||
| }); | ||||
|   | ||||
| @@ -85,9 +85,8 @@ const Indicator = new Lang.Class({ | ||||
|         // The menu only appears when airplane mode is on, so just | ||||
|         // statically build it as if it was on, rather than dynamically | ||||
|         // changing the menu contents. | ||||
|         this._item = new PopupMenu.PopupSubMenuMenuItem(_("Airplane Mode"), true); | ||||
|         this._item = new PopupMenu.PopupSubMenuMenuItem(_("Airplane Mode On"), true); | ||||
|         this._item.icon.icon_name = 'airplane-mode-symbolic'; | ||||
|         this._item.status.text = _("On"); | ||||
|         this._offItem = this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() { | ||||
|             this._manager.airplaneMode = false; | ||||
|         })); | ||||
|   | ||||
| @@ -17,15 +17,24 @@ const PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
|  | ||||
| const LOCKDOWN_SCHEMA = 'org.gnome.desktop.lockdown'; | ||||
| const SCREENSAVER_SCHEMA = 'org.gnome.desktop.screensaver'; | ||||
| const LOGIN_SCREEN_SCHEMA = 'org.gnome.login-screen'; | ||||
| const PRIVACY_SCHEMA = 'org.gnome.desktop.privacy' | ||||
| const DISABLE_USER_SWITCH_KEY = 'disable-user-switching'; | ||||
| const DISABLE_LOCK_SCREEN_KEY = 'disable-lock-screen'; | ||||
| const DISABLE_LOG_OUT_KEY = 'disable-log-out'; | ||||
| const DISABLE_RESTART_KEY = 'disable-restart-buttons'; | ||||
| const ALWAYS_SHOW_LOG_OUT_KEY = 'always-show-log-out'; | ||||
|  | ||||
| const SENSOR_BUS_NAME = 'net.hadess.SensorProxy'; | ||||
| const SENSOR_OBJECT_PATH = '/net/hadess/SensorProxy'; | ||||
|  | ||||
| const SensorProxyInterface = '<node> \ | ||||
| <interface name="net.hadess.SensorProxy"> \ | ||||
|   <property name="HasAccelerometer" type="b" access="read"/> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const SensorProxy = Gio.DBusProxy.makeProxyWrapper(SensorProxyInterface); | ||||
|  | ||||
| const AltSwitcher = new Lang.Class({ | ||||
|     Name: 'AltSwitcher', | ||||
|  | ||||
| @@ -95,10 +104,8 @@ const Indicator = new Lang.Class({ | ||||
|     _init: function() { | ||||
|         this.parent(); | ||||
|  | ||||
|         this._screenSaverSettings = new Gio.Settings({ schema_id: SCREENSAVER_SCHEMA }); | ||||
|         this._loginScreenSettings = new Gio.Settings({ schema_id: LOGIN_SCREEN_SCHEMA }); | ||||
|         this._lockdownSettings = new Gio.Settings({ schema_id: LOCKDOWN_SCHEMA }); | ||||
|         this._privacySettings = new Gio.Settings({ schema_id: PRIVACY_SCHEMA }); | ||||
|         this._orientationSettings = new Gio.Settings({ schema_id: 'org.gnome.settings-daemon.peripherals.touchscreen' }); | ||||
|  | ||||
|         this._session = new GnomeSession.SessionManager(); | ||||
| @@ -129,6 +136,7 @@ const Indicator = new Lang.Class({ | ||||
|                                 Lang.bind(this, this._updateMultiUser)); | ||||
|         this._updateSwitchUser(); | ||||
|         this._updateMultiUser(); | ||||
|         this._updateLockScreen(); | ||||
|  | ||||
|         // Whether shutdown is available or not depends on both lockdown | ||||
|         // settings (disable-log-out) and Polkit policy - the latter doesn't | ||||
| @@ -147,23 +155,32 @@ const Indicator = new Lang.Class({ | ||||
|  | ||||
|         this._orientationSettings.connect('changed::orientation-lock', | ||||
|                                           Lang.bind(this, this._updateOrientationLock)); | ||||
|         this._orientationExists = false; | ||||
|         Gio.DBus.session.watch_name('org.gnome.SettingsDaemon.Orientation', | ||||
|                                     Gio.BusNameWatcherFlags.NONE, | ||||
|                                     Lang.bind(this, function() { | ||||
|                                         this._orientationExists = true; | ||||
|                                         this._updateOrientationLock(); | ||||
|                                     }), | ||||
|                                     Lang.bind(this, function() { | ||||
|                                         this._orientationExists = false; | ||||
|                                         this._updateOrientationLock(); | ||||
|                                     })); | ||||
|         Gio.DBus.system.watch_name(SENSOR_BUS_NAME, | ||||
|                                    Gio.BusNameWatcherFlags.NONE, | ||||
|                                    Lang.bind(this, this._sensorProxyAppeared), | ||||
|                                    Lang.bind(this, function() { | ||||
|                                        this._sensorProxy = null; | ||||
|                                        this._updateOrientationLock(); | ||||
|                                    })); | ||||
|         this._updateOrientationLock(); | ||||
|  | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); | ||||
|         this._sessionUpdated(); | ||||
|     }, | ||||
|  | ||||
|     _sensorProxyAppeared: function() { | ||||
|         this._sensorProxy = new SensorProxy(Gio.DBus.system, SENSOR_BUS_NAME, SENSOR_OBJECT_PATH, | ||||
|             Lang.bind(this, function(proxy, error) { | ||||
|                 if (error) { | ||||
|                     log(error.message); | ||||
|                     return; | ||||
|                 } | ||||
|                 this._sensorProxy.connect('g-properties-changed', | ||||
|                                           Lang.bind(this, this._updateOrientationLock)); | ||||
|                 this._updateOrientationLock(); | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     _updateActionsVisibility: function() { | ||||
|         let visible = (this._settingsAction.visible || | ||||
|                        this._orientationLockAction.visible || | ||||
| @@ -234,13 +251,22 @@ const Indicator = new Lang.Class({ | ||||
|             let file = Gio.File.new_for_path(iconFile); | ||||
|             let gicon = new Gio.FileIcon({ file: file }); | ||||
|             this._switchUserSubMenu.icon.gicon = gicon; | ||||
|  | ||||
|             this._switchUserSubMenu.icon.add_style_class_name('user-icon'); | ||||
|             this._switchUserSubMenu.icon.remove_style_class_name('default-icon'); | ||||
|         } else { | ||||
|             this._switchUserSubMenu.icon.icon_name = 'avatar-default-symbolic'; | ||||
|  | ||||
|             this._switchUserSubMenu.icon.add_style_class_name('default-icon'); | ||||
|             this._switchUserSubMenu.icon.remove_style_class_name('user-icon'); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateOrientationLock: function() { | ||||
|         this._orientationLockAction.visible = this._orientationExists; | ||||
|         if (this._sensorProxy) | ||||
|             this._orientationLockAction.visible = this._sensorProxy.HasAccelerometer; | ||||
|         else | ||||
|             this._orientationLockAction.visible = false; | ||||
|  | ||||
|         let locked = this._orientationSettings.get_boolean('orientation-lock'); | ||||
|         let icon = this._orientationLockAction.child; | ||||
| @@ -324,6 +350,9 @@ const Indicator = new Lang.Class({ | ||||
|         this._switchUserSubMenu.menu.addMenuItem(item); | ||||
|         this._logoutItem = item; | ||||
|  | ||||
|         this._switchUserSubMenu.menu.addSettingsAction(_("Account Settings"), | ||||
|                                                        'gnome-user-accounts-panel.desktop'); | ||||
|  | ||||
|         this._user.connect('notify::is-loaded', Lang.bind(this, this._updateSwitchUserSubMenu)); | ||||
|         this._user.connect('changed', Lang.bind(this, this._updateSwitchUserSubMenu)); | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,7 @@ const Clutter = imports.gi.Clutter; | ||||
| const AccountsService = imports.gi.AccountsService; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GObject = imports.gi.GObject; | ||||
| const Lang = imports.lang; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| @@ -117,6 +118,7 @@ const UserWidgetLabel = new Lang.Class({ | ||||
|             this._currentLabel = this._realNameLabel; | ||||
|         else | ||||
|             this._currentLabel = this._userNameLabel; | ||||
|         this.label_actor = this._currentLabel; | ||||
|  | ||||
|         let childBox = new Clutter.ActorBox(); | ||||
|         childBox.x1 = 0; | ||||
| @@ -158,6 +160,9 @@ const UserWidget = new Lang.Class({ | ||||
|         this._label = new UserWidgetLabel(user); | ||||
|         this.actor.add_child(this._label); | ||||
|  | ||||
|         this._label.bind_property('label-actor', this.actor, 'label-actor', | ||||
|                                   GObject.BindingFlags.SYNC_CREATE); | ||||
|  | ||||
|         this._userLoadedId = this._user.connect('notify::is-loaded', Lang.bind(this, this._updateUser)); | ||||
|         this._userChangedId = this._user.connect('changed', Lang.bind(this, this._updateUser)); | ||||
|         this._updateUser(); | ||||
|   | ||||
| @@ -152,7 +152,7 @@ const ViewSelector = new Lang.Class({ | ||||
|  | ||||
|         this._workspacesDisplay = new WorkspacesView.WorkspacesDisplay(); | ||||
|         this._workspacesPage = this._addPage(this._workspacesDisplay.actor, | ||||
|                                              _("Windows"), 'emblem-documents-symbolic'); | ||||
|                                              _("Windows"), 'focus-windows-symbolic'); | ||||
|  | ||||
|         this.appDisplay = new AppDisplay.AppDisplay(); | ||||
|         this._appsPage = this._addPage(this.appDisplay.actor, | ||||
| @@ -214,10 +214,13 @@ const ViewSelector = new Lang.Class({ | ||||
|                               Shell.ActionMode.OVERVIEW, | ||||
|                               Lang.bind(Main.overview, Main.overview.toggle)); | ||||
|  | ||||
|         let gesture; | ||||
|  | ||||
|         gesture = new EdgeDragAction.EdgeDragAction(St.Side.LEFT, | ||||
|                                                     Shell.ActionMode.NORMAL); | ||||
|         let side; | ||||
|         if (Clutter.get_default_text_direction() == Clutter.TextDirection.RTL) | ||||
|             side = St.Side.RIGHT; | ||||
|         else | ||||
|             side = St.Side.LEFT; | ||||
|         let gesture = new EdgeDragAction.EdgeDragAction(side, | ||||
|                                                         Shell.ActionMode.NORMAL); | ||||
|         gesture.connect('activated', Lang.bind(this, function() { | ||||
|             if (Main.overview.visible) | ||||
|                 Main.overview.hide(); | ||||
| @@ -462,6 +465,12 @@ const ViewSelector = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _shouldTriggerSearch: function(symbol) { | ||||
|         if (symbol == Clutter.Multi_key) | ||||
|             return true; | ||||
|  | ||||
|         if (symbol == Clutter.BackSpace && this._searchActive) | ||||
|             return true; | ||||
|  | ||||
|         let unicode = Clutter.keysym_to_unicode(symbol); | ||||
|         if (unicode == 0) | ||||
|             return false; | ||||
| @@ -469,7 +478,7 @@ const ViewSelector = new Lang.Class({ | ||||
|         if (getTermsForSearchString(String.fromCharCode(unicode)).length > 0) | ||||
|             return true; | ||||
|  | ||||
|         return symbol == Clutter.BackSpace && this._searchActive; | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     startSearch: function(event) { | ||||
|   | ||||
| @@ -11,7 +11,8 @@ const WindowAttentionHandler = new Lang.Class({ | ||||
|  | ||||
|     _init : function() { | ||||
|         this._tracker = Shell.WindowTracker.get_default(); | ||||
|         global.display.connect('window-demands-attention', Lang.bind(this, this._onWindowDemandsAttention)); | ||||
|         this._windowDemandsAttentionId = global.display.connect('window-demands-attention', | ||||
|                                                                 Lang.bind(this, this._onWindowDemandsAttention)); | ||||
|     }, | ||||
|  | ||||
|     _getTitleAndBanner: function(app, window) { | ||||
|   | ||||
| @@ -19,8 +19,6 @@ const Tweener = imports.ui.tweener; | ||||
| const WindowMenu = imports.ui.windowMenu; | ||||
|  | ||||
| const SHELL_KEYBINDINGS_SCHEMA = 'org.gnome.shell.keybindings'; | ||||
| const MAXIMIZE_WINDOW_ANIMATION_TIME = 0.15; | ||||
| const UNMAXIMIZE_WINDOW_ANIMATION_TIME = 0.15; | ||||
| const MINIMIZE_WINDOW_ANIMATION_TIME = 0.2; | ||||
| const SHOW_WINDOW_ANIMATION_TIME = 0.15; | ||||
| const DIALOG_SHOW_WINDOW_ANIMATION_TIME = 0.1; | ||||
| @@ -83,12 +81,10 @@ const DisplayChangeDialog = new Lang.Class({ | ||||
|         */ | ||||
|         this._cancelButton = this.addButton({ label: _("Revert Settings"), | ||||
|                                               action: Lang.bind(this, this._onFailure), | ||||
|                                               key: Clutter.Escape }, | ||||
|                                             { expand: true, x_fill: false, x_align: St.Align.START }); | ||||
|                                               key: Clutter.Escape }); | ||||
|         this._okButton = this.addButton({ label:  _("Keep Changes"), | ||||
|                                           action: Lang.bind(this, this._onSuccess), | ||||
|                                           default: true }, | ||||
|                                         { expand: false, x_fill: false, x_align: St.Align.END }); | ||||
|                                           default: true }); | ||||
|  | ||||
|         this._timeoutId = Mainloop.timeout_add(ONE_SECOND, Lang.bind(this, this._tick)); | ||||
|         GLib.Source.set_name_by_id(this._timeoutId, '[gnome-shell] this._tick'); | ||||
| @@ -197,6 +193,8 @@ const WorkspaceTracker = new Lang.Class({ | ||||
|         this._workspaces = []; | ||||
|         this._checkWorkspacesId = 0; | ||||
|  | ||||
|         this._pauseWorkspaceCheck = false; | ||||
|  | ||||
|         let tracker = Shell.WindowTracker.get_default(); | ||||
|         tracker.connect('startup-sequence-changed', Lang.bind(this, this._queueCheckWorkspaces)); | ||||
|  | ||||
| @@ -215,11 +213,20 @@ const WorkspaceTracker = new Lang.Class({ | ||||
|  | ||||
|     _getWorkspaceSettings: function() { | ||||
|         let settings = global.get_overrides_settings(); | ||||
|         if (settings.list_keys().indexOf('dynamic-workspaces') > -1) | ||||
|         if (settings && | ||||
|             settings.settings_schema.list_keys().indexOf('dynamic-workspaces') > -1) | ||||
|             return settings; | ||||
|         return new Gio.Settings({ schema_id: 'org.gnome.mutter' }); | ||||
|     }, | ||||
|  | ||||
|     blockUpdates: function() { | ||||
|         this._pauseWorkspaceCheck = true; | ||||
|     }, | ||||
|  | ||||
|     unblockUpdates: function() { | ||||
|         this._pauseWorkspaceCheck = false; | ||||
|     }, | ||||
|  | ||||
|     _checkWorkspaces: function() { | ||||
|         let i; | ||||
|         let emptyWorkspaces = []; | ||||
| @@ -229,6 +236,10 @@ const WorkspaceTracker = new Lang.Class({ | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         // Update workspaces only if Dynamic Workspace Management has not been paused by some other function | ||||
|         if (this._pauseWorkspaceCheck) | ||||
|             return true; | ||||
|  | ||||
|         for (i = 0; i < this._workspaces.length; i++) { | ||||
|             let lastRemoved = this._workspaces[i]._lastRemovedWindow; | ||||
|             if ((lastRemoved && | ||||
| @@ -464,52 +475,100 @@ const TilePreview = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const TouchpadWorkspaceSwitchAction = new Lang.Class({ | ||||
|     Name: 'TouchpadWorkspaceSwitchAction', | ||||
|  | ||||
|     _init: function(actor) { | ||||
|         this._dx = 0; | ||||
|         this._dy = 0; | ||||
|         actor.connect('captured-event', Lang.bind(this, this._handleEvent)); | ||||
|     }, | ||||
|  | ||||
|     _checkActivated: function() { | ||||
|         const MOTION_THRESHOLD = 50; | ||||
|         let allowedModes = Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW; | ||||
|         let dir; | ||||
|  | ||||
|         if ((allowedModes & Main.actionMode) == 0) | ||||
|             return; | ||||
|  | ||||
|         if (this._dy < -MOTION_THRESHOLD) | ||||
|             dir = Meta.MotionDirection.DOWN; | ||||
|         else if (this._dy > MOTION_THRESHOLD) | ||||
|             dir = Meta.MotionDirection.UP; | ||||
|         else if (this._dx < -MOTION_THRESHOLD) | ||||
|             dir = Meta.MotionDirection.RIGHT; | ||||
|         else if (this._dx > MOTION_THRESHOLD) | ||||
|             dir = Meta.MotionDirection.LEFT; | ||||
|         else | ||||
|             return; | ||||
|  | ||||
|         this.emit('activated', dir); | ||||
|     }, | ||||
|  | ||||
|     _handleEvent: function(actor, event) { | ||||
|         if (event.type() != Clutter.EventType.TOUCHPAD_SWIPE) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (event.get_gesture_swipe_finger_count() != 4) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (event.get_gesture_phase() == Clutter.TouchpadGesturePhase.UPDATE) { | ||||
|             let [dx, dy] = event.get_gesture_motion_delta(event); | ||||
|  | ||||
|             this._dx += dx; | ||||
|             this._dy += dy; | ||||
|         } else { | ||||
|             if (event.get_gesture_phase() == Clutter.TouchpadGesturePhase.END) | ||||
|                 this._checkActivated(); | ||||
|  | ||||
|             this._dx = 0; | ||||
|             this._dy = 0; | ||||
|         } | ||||
|  | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(TouchpadWorkspaceSwitchAction.prototype); | ||||
|  | ||||
| const WorkspaceSwitchAction = new Lang.Class({ | ||||
|     Name: 'WorkspaceSwitchAction', | ||||
|     Extends: Clutter.GestureAction, | ||||
|     Extends: Clutter.SwipeAction, | ||||
|  | ||||
|     _init : function() { | ||||
|         const MOTION_THRESHOLD = 50; | ||||
|  | ||||
|         this.parent(); | ||||
|         this.set_n_touch_points(4); | ||||
|         this.set_threshold_trigger_distance(MOTION_THRESHOLD, MOTION_THRESHOLD); | ||||
|  | ||||
|         global.display.connect('grab-op-begin', Lang.bind(this, function() { | ||||
|             this.cancel(); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     vfunc_gesture_prepare : function(action, actor) { | ||||
|     vfunc_gesture_prepare : function(actor) { | ||||
|         let allowedModes = Shell.ActionMode.NORMAL | Shell.ActionMode.OVERVIEW; | ||||
|         return this.get_n_current_points() == this.get_n_touch_points() && | ||||
|                (allowedModes & Main.actionMode); | ||||
|  | ||||
|         if (!this.parent(actor)) | ||||
|             return false; | ||||
|  | ||||
|         return (allowedModes & Main.actionMode); | ||||
|     }, | ||||
|  | ||||
|     vfunc_gesture_end : function(action, actor) { | ||||
|         const MOTION_THRESHOLD = 50; | ||||
|     vfunc_swept : function(actor, direction) { | ||||
|         let dir; | ||||
|  | ||||
|         // Just check one touchpoint here | ||||
|         let [startX, startY] = this.get_press_coords(0); | ||||
|         let [x, y] = this.get_motion_coords(0); | ||||
|         let offsetX = x - startX; | ||||
|         let offsetY = y - startY; | ||||
|         let direction; | ||||
|         if (direction & Clutter.SwipeDirection.UP) | ||||
|             dir = Meta.MotionDirection.DOWN; | ||||
|         else if (direction & Clutter.SwipeDirection.DOWN) | ||||
|             dir = Meta.MotionDirection.UP; | ||||
|         else if (direction & Clutter.SwipeDirection.LEFT) | ||||
|             dir = Meta.MotionDirection.RIGHT; | ||||
|         else if (direction & Clutter.SwipeDirection.RIGHT) | ||||
|             dir = Meta.MotionDirection.LEFT; | ||||
|  | ||||
|         if (Math.abs(offsetX) < MOTION_THRESHOLD && | ||||
|             Math.abs(offsetY) < MOTION_THRESHOLD) | ||||
|             return; | ||||
|  | ||||
|         if (Math.abs(offsetY) > Math.abs(offsetX)) { | ||||
|             if (offsetY > 0) | ||||
|                 direction = Meta.MotionDirection.UP; | ||||
|             else | ||||
|                 direction = Meta.MotionDirection.DOWN; | ||||
|         } else { | ||||
|             if (offsetX > 0) | ||||
|                 direction = Meta.MotionDirection.LEFT; | ||||
|             else | ||||
|                 direction = Meta.MotionDirection.RIGHT; | ||||
|         } | ||||
|  | ||||
|         this.emit('activated', direction); | ||||
|         this.emit('activated', dir); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(WorkspaceSwitchAction.prototype); | ||||
| @@ -617,9 +676,8 @@ const WindowManager = new Lang.Class({ | ||||
|  | ||||
|         this._minimizing = []; | ||||
|         this._unminimizing = []; | ||||
|         this._maximizing = []; | ||||
|         this._unmaximizing = []; | ||||
|         this._mapping = []; | ||||
|         this._resizing = []; | ||||
|         this._destroying = []; | ||||
|         this._movingWindow = null; | ||||
|  | ||||
| @@ -627,14 +685,15 @@ const WindowManager = new Lang.Class({ | ||||
|  | ||||
|         this._allowedKeybindings = {}; | ||||
|  | ||||
|         this._isWorkspacePrepended = false; | ||||
|  | ||||
|         this._switchData = null; | ||||
|         this._shellwm.connect('kill-switch-workspace', Lang.bind(this, this._switchWorkspaceDone)); | ||||
|         this._shellwm.connect('kill-window-effects', Lang.bind(this, function (shellwm, actor) { | ||||
|             this._minimizeWindowDone(shellwm, actor); | ||||
|             this._maximizeWindowDone(shellwm, actor); | ||||
|             this._unmaximizeWindowDone(shellwm, actor); | ||||
|             this._mapWindowDone(shellwm, actor); | ||||
|             this._destroyWindowDone(shellwm, actor); | ||||
|             this._sizeChangeWindowDone(shellwm, actor); | ||||
|         })); | ||||
|  | ||||
|         this._shellwm.connect('switch-workspace', Lang.bind(this, this._switchWorkspace)); | ||||
| @@ -643,8 +702,7 @@ const WindowManager = new Lang.Class({ | ||||
|         this._shellwm.connect('show-window-menu', Lang.bind(this, this._showWindowMenu)); | ||||
|         this._shellwm.connect('minimize', Lang.bind(this, this._minimizeWindow)); | ||||
|         this._shellwm.connect('unminimize', Lang.bind(this, this._unminimizeWindow)); | ||||
|         this._shellwm.connect('maximize', Lang.bind(this, this._maximizeWindow)); | ||||
|         this._shellwm.connect('unmaximize', Lang.bind(this, this._unmaximizeWindow)); | ||||
|         this._shellwm.connect('size-change', Lang.bind(this, this._sizeChangeWindow)); | ||||
|         this._shellwm.connect('map', Lang.bind(this, this._mapWindow)); | ||||
|         this._shellwm.connect('destroy', Lang.bind(this, this._destroyWindow)); | ||||
|         this._shellwm.connect('filter-keybinding', Lang.bind(this, this._filterKeybinding)); | ||||
| @@ -840,6 +898,7 @@ const WindowManager = new Lang.Class({ | ||||
|                            new Gio.Settings({ schema_id: SHELL_KEYBINDINGS_SCHEMA }), | ||||
|                            Meta.KeyBindingFlags.NONE, | ||||
|                            Shell.ActionMode.NORMAL | | ||||
|                            Shell.ActionMode.OVERVIEW | | ||||
|                            Shell.ActionMode.POPUP, | ||||
|                            Lang.bind(this, this._toggleCalendar)); | ||||
|  | ||||
| @@ -863,15 +922,22 @@ const WindowManager = new Lang.Class({ | ||||
|                                                 false, -1, 1); | ||||
|  | ||||
|         let gesture = new WorkspaceSwitchAction(); | ||||
|         gesture.connect('activated', Lang.bind(this, function(action, direction) { | ||||
|             let newWs = global.screen.get_active_workspace().get_neighbor(direction); | ||||
|             this.actionMoveWorkspace(newWs); | ||||
|         })); | ||||
|         gesture.connect('activated', Lang.bind(this, this._actionSwitchWorkspace)); | ||||
|         global.stage.add_action(gesture); | ||||
|  | ||||
|         // This is not a normal Clutter.GestureAction, doesn't need add_action() | ||||
|         gesture = new TouchpadWorkspaceSwitchAction(global.stage); | ||||
|         gesture.connect('activated', Lang.bind(this, this._actionSwitchWorkspace)); | ||||
|  | ||||
|         gesture = new AppSwitchAction(); | ||||
|         gesture.connect('activated', Lang.bind(this, this._switchApp)); | ||||
|         global.stage.add_action(gesture); | ||||
|  | ||||
|     }, | ||||
|  | ||||
|     _actionSwitchWorkspace: function(action, direction) { | ||||
|             let newWs = global.screen.get_active_workspace().get_neighbor(direction); | ||||
|             this.actionMoveWorkspace(newWs); | ||||
|     }, | ||||
|  | ||||
|     _lookupIndex: function (windows, metaWindow) { | ||||
| @@ -914,6 +980,8 @@ const WindowManager = new Lang.Class({ | ||||
|         if (!Meta.prefs_get_dynamic_workspaces()) | ||||
|             return; | ||||
|  | ||||
|         global.screen.append_new_workspace(false, global.get_current_time()); | ||||
|  | ||||
|         let windows = global.get_window_actors().map(function(winActor) { | ||||
|             return winActor.meta_window; | ||||
|         }); | ||||
| @@ -1151,22 +1219,119 @@ const WindowManager = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _sizeChangeWindow : function(shellwm, actor, whichChange, oldFrameRect, oldBufferRect) { | ||||
|         let types = [Meta.WindowType.NORMAL]; | ||||
|         if (!this._shouldAnimateActor(actor, types)) { | ||||
|             shellwm.completed_size_change(actor); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|     _maximizeWindow : function(shellwm, actor, targetX, targetY, targetWidth, targetHeight) { | ||||
|         shellwm.completed_maximize(actor); | ||||
|         if (whichChange == Meta.SizeChange.FULLSCREEN) | ||||
|             this._fullscreenWindow(shellwm, actor, oldFrameRect, oldBufferRect); | ||||
|         else if (whichChange == Meta.SizeChange.UNFULLSCREEN) | ||||
|             this._unfullscreenWindow(shellwm, actor, oldFrameRect, oldBufferRect); | ||||
|         else | ||||
|             shellwm.completed_size_change(actor); | ||||
|     }, | ||||
|  | ||||
|     _maximizeWindowDone : function(shellwm, actor) { | ||||
|     _fullscreenWindow: function(shellwm, actor, oldFrameRect, oldBufferRect) { | ||||
|         let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()]; | ||||
|         actor.translation_x = oldFrameRect.x - monitor.x; | ||||
|         actor.translation_y = oldFrameRect.y - monitor.y; | ||||
|         this._fullscreenAnimation(shellwm, actor, oldFrameRect); | ||||
|     }, | ||||
|  | ||||
|     _maximizeWindowOverwrite : function(shellwm, actor) { | ||||
|     _unfullscreenWindow: function(shellwm, actor, oldFrameRect, oldBufferRect) { | ||||
|         let targetRect = actor.meta_window.get_frame_rect(); | ||||
|         let monitor = Main.layoutManager.monitors[actor.meta_window.get_monitor()]; | ||||
|         actor.translation_x = -(targetRect.x - monitor.x); | ||||
|         actor.translation_y = -(targetRect.y - monitor.y); | ||||
|         this._fullscreenAnimation(shellwm, actor, oldFrameRect); | ||||
|     }, | ||||
|  | ||||
|     _unmaximizeWindow : function(shellwm, actor, targetX, targetY, targetWidth, targetHeight) { | ||||
|         shellwm.completed_unmaximize(actor); | ||||
|     _fullscreenAnimation: function(shellwm, actor, oldFrameRect) { | ||||
|         this._resizing.push(actor); | ||||
|  | ||||
|         // Position a clone of the window on top of the old position, | ||||
|         // while actor updates are frozen. | ||||
|         // Note that the MetaWindow has up to date sizing information for | ||||
|         // the new geometry already. | ||||
|         let targetRect = actor.meta_window.get_frame_rect(); | ||||
|         let actorContent = Shell.util_get_content_for_window_actor(actor, oldFrameRect); | ||||
|         let actorClone = new St.Widget({ content: actorContent }); | ||||
|         actorClone.set_offscreen_redirect(Clutter.OffscreenRedirect.ALWAYS); | ||||
|         actorClone.set_position(oldFrameRect.x, oldFrameRect.y); | ||||
|         actorClone.set_size(oldFrameRect.width, oldFrameRect.height); | ||||
|         Main.uiGroup.add_actor(actorClone); | ||||
|  | ||||
|         actor.__fullscreenClone = actorClone; | ||||
|  | ||||
|         let scaleX = targetRect.width / oldFrameRect.width; | ||||
|         let scaleY = targetRect.height / oldFrameRect.height; | ||||
|  | ||||
|         // Now scale and fade out the clone | ||||
|         Tweener.addTween(actorClone, | ||||
|                          { x: targetRect.x, | ||||
|                            y: targetRect.y, | ||||
|                            scale_x: scaleX, | ||||
|                            scale_y: scaleY, | ||||
|                            opacity: 0, | ||||
|                            time: WINDOW_ANIMATION_TIME, | ||||
|                            transition: 'easeOutQuad' | ||||
|                          }); | ||||
|  | ||||
|         // Now set scale the actor to size it as the clone. | ||||
|         // Note that the caller of this function already set a translation | ||||
|         // on the actor. | ||||
|         actor.scale_x = 1 / scaleX; | ||||
|         actor.scale_y = 1 / scaleY; | ||||
|  | ||||
|         // Scale it to its actual new size | ||||
|         Tweener.addTween(actor, | ||||
|                          { scale_x: 1.0, | ||||
|                            scale_y: 1.0, | ||||
|                            translation_x: 0, | ||||
|                            translation_y: 0, | ||||
|                            time: WINDOW_ANIMATION_TIME, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onComplete: this._sizeChangeWindowDone, | ||||
|                            onCompleteScope: this, | ||||
|                            onCompleteParams: [shellwm, actor], | ||||
|                            onOverwrite: this._sizeChangeWindowOverwritten, | ||||
|                            onOverwriteScope: this, | ||||
|                            onOverwriteParams: [shellwm, actor] | ||||
|                          }); | ||||
|  | ||||
|         // Now unfreeze actor updates, to get it to the new size. | ||||
|         // It's important that we don't wait until the animation is completed to | ||||
|         // do this, otherwise our scale will be applied to the old texture size. | ||||
|         shellwm.completed_size_change(actor); | ||||
|     }, | ||||
|  | ||||
|     _unmaximizeWindowDone : function(shellwm, actor) { | ||||
|     _sizeChangeWindowDone: function(shellwm, actor) { | ||||
|         if (this._removeEffect(this._resizing, actor)) { | ||||
|             Tweener.removeTweens(actor); | ||||
|             actor.scale_x = 1.0; | ||||
|             actor.scale_y = 1.0; | ||||
|             actor.translation_x = 0; | ||||
|             actor.translation_y = 0; | ||||
|  | ||||
|             let actorClone = actor.__fullscreenClone; | ||||
|             if (actorClone) { | ||||
|                 actorClone.destroy(); | ||||
|                 delete actor.__fullscreenClone; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _sizeChangeWindowOverwritten: function(shellwm, actor) { | ||||
|         if (this._removeEffect(this._resizing, actor)) { | ||||
|             let actorClone = actor.__fullscreenClone; | ||||
|             if (actorClone) { | ||||
|                 actorClone.destroy(); | ||||
|                 delete actor.__fullscreenClone; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _hasAttachedDialogs: function(window, ignoreWindow) { | ||||
| @@ -1625,14 +1790,26 @@ const WindowManager = new Lang.Class({ | ||||
|         let newWs; | ||||
|         let direction; | ||||
|  | ||||
|         if (action == 'move') { | ||||
|             // "Moving" a window to another workspace doesn't make sense when | ||||
|             // it cannot be unstuck, and is potentially confusing if a new | ||||
|             // workspaces is added at the start/end | ||||
|             if (window.is_always_on_all_workspaces() || | ||||
|                 (Meta.prefs_get_workspaces_only_on_primary() && | ||||
|                  window.get_monitor() != Main.layoutManager.primaryIndex)) | ||||
|               return; | ||||
|         } | ||||
|  | ||||
|         if (target == 'last') { | ||||
|             direction = Meta.MotionDirection.DOWN; | ||||
|             newWs = screen.get_workspace_by_index(screen.n_workspaces - 1); | ||||
|         } else if (isNaN(target)) { | ||||
|             // Prepend a new workspace dynamically | ||||
|             if (screen.get_active_workspace_index() == 0 && | ||||
|                 action == 'move' && target == 'up') | ||||
|                 action == 'move' && target == 'up' && this._isWorkspacePrepended == false) { | ||||
|                 this.insertWorkspace(0); | ||||
|                 this._isWorkspacePrepended = true; | ||||
|             } | ||||
|  | ||||
|             direction = Meta.MotionDirection[target.toUpperCase()]; | ||||
|             newWs = screen.get_active_workspace().get_neighbor(direction); | ||||
| @@ -1657,9 +1834,12 @@ const WindowManager = new Lang.Class({ | ||||
|  | ||||
|         if (!Main.overview.visible) { | ||||
|             if (this._workspaceSwitcherPopup == null) { | ||||
|                 this._workspaceTracker.blockUpdates(); | ||||
|                 this._workspaceSwitcherPopup = new WorkspaceSwitcherPopup.WorkspaceSwitcherPopup(); | ||||
|                 this._workspaceSwitcherPopup.connect('destroy', Lang.bind(this, function() { | ||||
|                     this._workspaceTracker.unblockUpdates(); | ||||
|                     this._workspaceSwitcherPopup = null; | ||||
|                     this._isWorkspacePrepended = false; | ||||
|                 })); | ||||
|             } | ||||
|             this._workspaceSwitcherPopup.display(direction, newWs.index()); | ||||
|   | ||||
| @@ -74,7 +74,7 @@ const WindowMenu = new Lang.Class({ | ||||
|                 window.make_above(); | ||||
|         })); | ||||
|         if (window.is_above()) | ||||
|             item.setOrnament(PopupMenu.Ornament.DOT); | ||||
|             item.setOrnament(PopupMenu.Ornament.CHECK); | ||||
|         if (window.get_maximized() == Meta.MaximizeFlags.BOTH || | ||||
|             type == Meta.WindowType.DOCK || | ||||
|             type == Meta.WindowType.DESKTOP || | ||||
| @@ -93,7 +93,7 @@ const WindowMenu = new Lang.Class({ | ||||
|                     window.stick(); | ||||
|             })); | ||||
|             if (isSticky) | ||||
|                 item.setOrnament(PopupMenu.Ornament.DOT); | ||||
|                 item.setOrnament(PopupMenu.Ornament.CHECK); | ||||
|             if (window.is_always_on_all_workspaces()) | ||||
|                 item.setSensitive(false); | ||||
|  | ||||
| @@ -101,13 +101,22 @@ const WindowMenu = new Lang.Class({ | ||||
|  | ||||
|             if (!isSticky) { | ||||
|                 let workspace = window.get_workspace(); | ||||
|                 let idx = workspace.index(); | ||||
|                 if (idx > 0) { | ||||
|                 if (workspace != workspace.get_neighbor(Meta.MotionDirection.LEFT)) { | ||||
|                      this.addAction(_("Move to Workspace Left"), Lang.bind(this, function(event) { | ||||
|                         window.change_workspace(workspace.get_neighbor(Meta.MotionDirection.LEFT)); | ||||
|                     })); | ||||
|                 } | ||||
|                 if (workspace != workspace.get_neighbor(Meta.MotionDirection.RIGHT)) { | ||||
|                      this.addAction(_("Move to Workspace Right"), Lang.bind(this, function(event) { | ||||
|                         window.change_workspace(workspace.get_neighbor(Meta.MotionDirection.RIGHT)); | ||||
|                     })); | ||||
|                 } | ||||
|                 if (workspace != workspace.get_neighbor(Meta.MotionDirection.UP)) { | ||||
|                     this.addAction(_("Move to Workspace Up"), Lang.bind(this, function(event) { | ||||
|                         window.change_workspace(workspace.get_neighbor(Meta.MotionDirection.UP)); | ||||
|                     })); | ||||
|                 } | ||||
|                 if (idx < nWorkspaces) { | ||||
|                 if (workspace != workspace.get_neighbor(Meta.MotionDirection.DOWN)) { | ||||
|                      this.addAction(_("Move to Workspace Down"), Lang.bind(this, function(event) { | ||||
|                         window.change_workspace(workspace.get_neighbor(Meta.MotionDirection.DOWN)); | ||||
|                     })); | ||||
| @@ -115,6 +124,39 @@ const WindowMenu = new Lang.Class({ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         let screen = global.screen; | ||||
|         let nMonitors = screen.get_n_monitors(); | ||||
|         if (nMonitors > 1) { | ||||
|           this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|  | ||||
|           let monitorIndex = window.get_monitor(); | ||||
|  | ||||
|           let upMonitorIndex = screen.get_monitor_neighbor_index(monitorIndex, Meta.ScreenDirection.UP); | ||||
|           if (upMonitorIndex != -1) { | ||||
|             this.addAction(_("Move to Monitor Up"), Lang.bind(this, function(event) { | ||||
|               window.move_to_monitor(upMonitorIndex); | ||||
|             })); | ||||
|           } | ||||
|           let downMonitorIndex = screen.get_monitor_neighbor_index(monitorIndex, Meta.ScreenDirection.DOWN); | ||||
|           if (downMonitorIndex != -1) { | ||||
|             this.addAction(_("Move to Monitor Down"), Lang.bind(this, function(event) { | ||||
|               window.move_to_monitor(downMonitorIndex); | ||||
|             })); | ||||
|           } | ||||
|           let leftMonitorIndex = screen.get_monitor_neighbor_index(monitorIndex, Meta.ScreenDirection.LEFT); | ||||
|           if (leftMonitorIndex != -1) { | ||||
|             this.addAction(_("Move to Monitor Left"), Lang.bind(this, function(event) { | ||||
|               window.move_to_monitor(leftMonitorIndex); | ||||
|             })); | ||||
|           } | ||||
|           let rightMonitorIndex = screen.get_monitor_neighbor_index(monitorIndex, Meta.ScreenDirection.RIGHT); | ||||
|           if (rightMonitorIndex != -1) { | ||||
|             this.addAction(_("Move to Monitor Right"), Lang.bind(this, function(event) { | ||||
|               window.move_to_monitor(rightMonitorIndex); | ||||
|             })); | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|  | ||||
|         item = this.addAction(_("Close"), Lang.bind(this, function(event) { | ||||
| @@ -167,8 +209,12 @@ const WindowMenuManager = new Lang.Class({ | ||||
|         menu.connect('activate', function() { | ||||
|             window.check_alive(global.get_current_time()); | ||||
|         }); | ||||
|         let destroyId = window.connect('unmanaged', | ||||
|             function() { | ||||
|                 menu.close(); | ||||
|             }); | ||||
|  | ||||
|         this._sourceActor.set_size(rect.width, rect.height); | ||||
|         this._sourceActor.set_size(Math.max(1, rect.width), Math.max(1, rect.height)); | ||||
|         this._sourceActor.set_position(rect.x, rect.y); | ||||
|         this._sourceActor.show(); | ||||
|  | ||||
| @@ -180,6 +226,7 @@ const WindowMenuManager = new Lang.Class({ | ||||
|  | ||||
|             this._sourceActor.hide(); | ||||
|             menu.destroy(); | ||||
|             window.disconnect(destroyId); | ||||
|         })); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -361,6 +361,9 @@ const WindowClone = new Lang.Class({ | ||||
|         // a long-press canceled when the pointer movement | ||||
|         // exceeds dnd-drag-threshold to manually start the drag | ||||
|         if (state == Clutter.LongPressState.CANCEL) { | ||||
|             let event = Clutter.get_current_event(); | ||||
|             this._dragTouchSequence = event.get_event_sequence(); | ||||
|  | ||||
|             // A click cancels a long-press before any click handler is | ||||
|             // run - make sure to not start a drag in that case | ||||
|             Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, | ||||
| @@ -369,7 +372,7 @@ const WindowClone = new Lang.Class({ | ||||
|                         return; | ||||
|                     let [x, y] = action.get_coords(); | ||||
|                     action.release(); | ||||
|                     this._draggable.startDrag(x, y, global.get_current_time()); | ||||
|                     this._draggable.startDrag(x, y, global.get_current_time(), this._dragTouchSequence); | ||||
|                 })); | ||||
|         } | ||||
|         return true; | ||||
|   | ||||
| @@ -80,6 +80,8 @@ const WindowClone = new Lang.Class({ | ||||
|  | ||||
|         this.actor.connect('button-release-event', | ||||
|                            Lang.bind(this, this._onButtonRelease)); | ||||
|         this.actor.connect('touch-event', | ||||
|                            Lang.bind(this, this._onTouchEvent)); | ||||
|  | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|  | ||||
| @@ -200,6 +202,15 @@ const WindowClone = new Lang.Class({ | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _onTouchEvent : function (actor, event) { | ||||
|         if (event.type() != Clutter.EventType.TOUCH_END || | ||||
|             !global.display.is_pointer_emulating_sequence(event.get_event_sequence())) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         this.emit('selected', event.get_time()); | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _onDragBegin : function (draggable, time) { | ||||
|         this.inDrag = true; | ||||
|         this.emit('drag-begin'); | ||||
| @@ -642,6 +653,7 @@ const ThumbnailsBox = new Lang.Class({ | ||||
|  | ||||
|         this.actor.connect('button-press-event', function() { return Clutter.EVENT_STOP; }); | ||||
|         this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease)); | ||||
|         this.actor.connect('touch-event', Lang.bind(this, this._onTouchEvent)); | ||||
|  | ||||
|         Main.overview.connect('showing', | ||||
|                               Lang.bind(this, this._createThumbnails)); | ||||
| @@ -672,18 +684,31 @@ const ThumbnailsBox = new Lang.Class({ | ||||
|                 global.screen.n_workspaces > 1; | ||||
|     }, | ||||
|  | ||||
|     _onButtonRelease: function(actor, event) { | ||||
|         let [stageX, stageY] = event.get_coords(); | ||||
|     _activateThumbnailAtPoint: function (stageX, stageY, time) { | ||||
|         let [r, x, y] = this.actor.transform_stage_point(stageX, stageY); | ||||
|  | ||||
|         for (let i = 0; i < this._thumbnails.length; i++) { | ||||
|             let thumbnail = this._thumbnails[i] | ||||
|             let [w, h] = thumbnail.actor.get_transformed_size(); | ||||
|             if (y >= thumbnail.actor.y && y <= thumbnail.actor.y + h) { | ||||
|                 thumbnail.activate(event.get_time()); | ||||
|                 thumbnail.activate(time); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onButtonRelease: function(actor, event) { | ||||
|         let [stageX, stageY] = event.get_coords(); | ||||
|         this._activateThumbnailAtPoint(stageX, stageY, event.get_time()); | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _onTouchEvent: function (actor, event) { | ||||
|         if (event.type() == Clutter.EventType.TOUCH_END && | ||||
|             global.display.is_pointer_emulating_sequence(event.get_event_sequence())) { | ||||
|             let [stageX, stageY] = event.get_coords(); | ||||
|             this._activateThumbnailAtPoint(stageX, stageY, event.get_time()); | ||||
|         } | ||||
|  | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|   | ||||
| @@ -97,8 +97,6 @@ const WorkspacesView = new Lang.Class({ | ||||
|         this._scrolling = false; // swipe-scrolling | ||||
|         this._animatingScroll = false; // programatically updating the adjustment | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema_id: OVERRIDE_SCHEMA }); | ||||
|  | ||||
|         let activeWorkspaceIndex = global.screen.get_active_workspace_index(); | ||||
|         this.scrollAdjustment = new St.Adjustment({ value: activeWorkspaceIndex, | ||||
|                                                     lower: 0, | ||||
| @@ -418,12 +416,12 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|         this.actor.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesActualGeometry)); | ||||
|         this.actor.connect('parent-set', Lang.bind(this, this._parentSet)); | ||||
|  | ||||
|         let clickAction = new Clutter.ClickAction() | ||||
|         let clickAction = new Clutter.ClickAction(); | ||||
|         clickAction.connect('clicked', Lang.bind(this, function(action) { | ||||
|             // Only switch to the workspace when there's no application | ||||
|             // windows open. The problem is that it's too easy to miss | ||||
|             // an app window and get the wrong one focused. | ||||
|             if (action.get_button() == 1 && | ||||
|             if ((action.get_button() == 1 || action.get_button() == 0) && | ||||
|                 this._getPrimaryView().getActiveWorkspace().isEmpty()) | ||||
|                 Main.overview.hide(); | ||||
|         })); | ||||
| @@ -464,6 +462,7 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|  | ||||
|         this._notifyOpacityId = 0; | ||||
|         this._scrollEventId = 0; | ||||
|         this._keyPressEventId = 0; | ||||
|  | ||||
|         this._fullGeometry = null; | ||||
|     }, | ||||
| @@ -495,6 +494,9 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|                                   Lang.bind(this, this._onRestacked)); | ||||
|         if (this._scrollEventId == 0) | ||||
|             this._scrollEventId = Main.overview.connect('scroll-event', Lang.bind(this, this._onScrollEvent)); | ||||
|  | ||||
|         if (this._keyPressEventId == 0) | ||||
|             this._keyPressEventId = global.stage.connect('key-press-event', Lang.bind(this, this._onKeyPressEvent)); | ||||
|     }, | ||||
|  | ||||
|     animateFromOverview: function(fadeOnPrimary) { | ||||
| @@ -517,7 +519,10 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|             Main.overview.disconnect(this._scrollEventId); | ||||
|             this._scrollEventId = 0; | ||||
|         } | ||||
|  | ||||
|         if (this._keyPressEventId > 0) { | ||||
|             global.stage.disconnect(this._keyPressEventId); | ||||
|             this._keyPressEventId = 0; | ||||
|         } | ||||
|         for (let i = 0; i < this._workspacesViews.length; i++) | ||||
|             this._workspacesViews[i].destroy(); | ||||
|         this._workspacesViews = []; | ||||
| @@ -670,6 +675,25 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|         } | ||||
|         Main.wm.actionMoveWorkspace(ws); | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _onKeyPressEvent: function(actor, event) { | ||||
|         if (!this.actor.mapped) | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         let activeWs = global.screen.get_active_workspace(); | ||||
|         let ws; | ||||
|         switch (event.get_key_symbol()) { | ||||
|         case Clutter.KEY_Page_Up: | ||||
|             ws = activeWs.get_neighbor(Meta.MotionDirection.UP); | ||||
|             break; | ||||
|         case Clutter.KEY_Page_Down: | ||||
|             ws = activeWs.get_neighbor(Meta.MotionDirection.DOWN); | ||||
|             break; | ||||
|         default: | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         } | ||||
|         Main.wm.actionMoveWorkspace(ws); | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(WorkspacesDisplay.prototype); | ||||
|   | ||||
| @@ -140,7 +140,7 @@ | ||||
|  | ||||
|                 <para> | ||||
|                         <filename>/usr/share/gnome-session/sessions/gnome.session</filename>, | ||||
|                         <filename>/usr/share/applications/gnome-shell.desktop</filename>.</para> | ||||
|                         <filename>/usr/share/applications/org.gnome.Shell.desktop</filename>.</para> | ||||
|         </refsect1> | ||||
|  | ||||
|         <refsect1> | ||||
|   | ||||
| @@ -7,6 +7,7 @@ be | ||||
| bg | ||||
| bn | ||||
| bn_IN | ||||
| bs | ||||
| ca | ||||
| ca@valencia | ||||
| cs | ||||
| @@ -23,6 +24,7 @@ fi | ||||
| fr | ||||
| fur | ||||
| ga | ||||
| gd | ||||
| gl | ||||
| gu | ||||
| he | ||||
| @@ -49,6 +51,7 @@ nb | ||||
| ne | ||||
| nl | ||||
| nn | ||||
| oc | ||||
| or | ||||
| pa | ||||
| pl | ||||
|   | ||||
| @@ -2,9 +2,8 @@ | ||||
| # Please keep this file sorted alphabetically. | ||||
| [encoding: UTF-8] | ||||
| data/50-gnome-shell-system.xml.in | ||||
| data/gnome-shell.desktop.in.in | ||||
| data/gnome-shell-extension-prefs.desktop.in.in | ||||
| data/gnome-shell-wayland.desktop.in.in | ||||
| data/org.gnome.Shell.desktop.in.in | ||||
| data/org.gnome.shell.gschema.xml.in.in | ||||
| data/org.gnome.Shell.PortalHelper.desktop.in | ||||
| js/extensionPrefs/main.js | ||||
| @@ -30,6 +29,7 @@ js/ui/endSessionDialog.js | ||||
| js/ui/extensionDownloader.js | ||||
| js/ui/extensionSystem.js | ||||
| js/ui/keyboard.js | ||||
| js/ui/legacyTray.js | ||||
| js/ui/lookingGlass.js | ||||
| js/ui/main.js | ||||
| js/ui/messageTray.js | ||||
|   | ||||