Compare commits
	
		
			549 Commits
		
	
	
		
			3.11.2
			...
			gnome-3-12
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|   | 1e047af492 | ||
|   | 0546819e8f | ||
|   | 62a6519db6 | ||
|   | a63821a57f | ||
|   | b7d2191059 | ||
|   | 68f5674e62 | ||
|   | ffa8ff730e | ||
|   | 80f9d1ff5d | ||
|   | 547005b3a2 | ||
|   | 36f051044c | ||
|   | 0d75416d80 | ||
|   | c96f95ab90 | ||
|   | 5ccbd3a821 | ||
|   | c9bd1d79cc | ||
|   | 07e6219704 | ||
|   | 8625d5b0cc | ||
|   | e021ca2135 | ||
|   | ba6a2cae9c | ||
|   | 5852c17f76 | ||
|   | 4fe0353991 | ||
|   | 3c4a898a91 | ||
|   | 0006feec67 | ||
|   | dd2ace2e37 | ||
|   | 0327bd0a9d | ||
|   | 42619cdd3b | ||
|   | 76ea1f4dca | ||
|   | e4e3252ffc | ||
|   | e9ae77186d | ||
|   | a0c146bbfd | ||
|   | 6d679148b6 | ||
|   | 07198c5890 | ||
|   | 4720bc3412 | ||
|   | 7113821964 | ||
|   | c2ae98209d | ||
|   | 21f0e422c0 | ||
|   | eb0f2b8b34 | ||
|   | 65e781a973 | ||
|   | 4590903159 | ||
|   | 8b6fcbea50 | ||
|   | c6067ffa1d | ||
|   | 7b4254da4e | ||
|   | 2f576e3076 | ||
|   | 60d7c3ad86 | ||
|   | 43fdc15ac0 | ||
|   | f082a58071 | ||
|   | ae5dfb5740 | ||
|   | e8fdba4f66 | ||
|   | 1030cdfe8a | ||
|   | ca2d626b59 | ||
|   | 2b4eaf6b3f | ||
|   | 6ce6e77d2a | ||
|   | 0117fcb0e7 | ||
|   | 79bebe849d | ||
|   | 25eadc5559 | ||
|   | a5784484e0 | ||
|   | a701b006c5 | ||
|   | 2d68bbf94e | ||
|   | df305314c1 | ||
|   | d03239c009 | ||
|   | 71ccad4399 | ||
|   | c916d43688 | ||
|   | 3e6d0bc252 | ||
|   | 79d0a848a4 | ||
|   | a6af33d450 | ||
|   | c6664adcce | ||
|   | 4e8a9470d1 | ||
|   | 1b88df9439 | ||
|   | ec42278654 | ||
|   | f435f249d0 | ||
|   | 9c88fec4fc | ||
|   | 9ecf466ce1 | ||
|   | a22fdea0e3 | ||
|   | 90e52d7266 | ||
|   | 74d9b6c2bf | ||
|   | b1e9873de5 | ||
|   | 6fdc52a64a | ||
|   | b7d6792de9 | ||
|   | c78dc55e65 | ||
|   | af74bded14 | ||
|   | e3c9a9c3e4 | ||
|   | c68eecaf1c | ||
|   | b0bdf7f6c3 | ||
|   | d0f69a72dc | ||
|   | 8d8c75d32d | ||
|   | e339e2658d | ||
|   | bc4a75a732 | ||
|   | e09e1bc3f5 | ||
|   | 87c50eb495 | ||
|   | be291ee4f9 | ||
|   | e634b49859 | ||
|   | 4dddaefa41 | ||
|   | cda60455f0 | ||
|   | a42b8870b0 | ||
|   | daa66a6de6 | ||
|   | 6c6aed84bc | ||
|   | c59314acc1 | ||
|   | dd3cc78be5 | ||
|   | 54b0b6eec5 | ||
|   | 41654b22b3 | ||
|   | 07cc60d65a | ||
|   | a7f9dc5114 | ||
|   | b5ae23d544 | ||
|   | 75347cb4f7 | ||
|   | 03a44b6ec2 | ||
|   | 17d2349c49 | ||
|   | 4e98c44052 | ||
|   | 0bef281d66 | ||
|   | 54af25ec24 | ||
|   | 86ab02f400 | ||
|   | ae01cd143f | ||
|   | 2974b29f15 | ||
|   | 1b78dd662b | ||
|   | c22264a0ca | ||
|   | 48c3e3f534 | ||
|   | 6ccc134ba6 | ||
|   | 9aa36d7851 | ||
|   | 3278f77739 | ||
|   | 5d24f48e3b | ||
|   | 573c1c86cc | ||
|   | 374c5967ba | ||
|   | 66b71a36ce | ||
|   | e70fd5a57a | ||
|   | 4589ce4d78 | ||
|   | d6197b0904 | ||
|   | 30fb2b0d99 | ||
|   | 5cdefc324d | ||
|   | b222d0fe44 | ||
|   | fe4fddf0d5 | ||
|   | c675c93733 | ||
|   | 29485ff24b | ||
|   | f6ed3d9f88 | ||
|   | 39a36cb510 | ||
|   | ff5550c82b | ||
|   | 7d5ce1a159 | ||
|   | c492415386 | ||
|   | 5616bbd45b | ||
|   | e117aa5297 | ||
|   | 17ac1382df | ||
|   | 057a026ea4 | ||
|   | 6ce6e86318 | ||
|   | 492558a2d2 | ||
|   | b78e00f372 | ||
|   | c2cc504837 | ||
|   | ac76940530 | ||
|   | 55d1c7e2ab | ||
|   | fdf264ff64 | ||
|   | e917b7ce0f | ||
|   | ec6facb9e7 | ||
|   | 60f3c09f90 | ||
|   | afdfd6cebc | ||
|   | edd66c40d9 | ||
|   | fc4bc5277a | ||
|   | 821768a414 | ||
|   | 522f3bf171 | ||
|   | fb7400ab85 | ||
|   | 210128f22b | ||
|   | 78ae233823 | ||
|   | 8f25da7cea | ||
|   | 7f52fdb435 | ||
|   | 0ba05b29b9 | ||
|   | ef8123e3a2 | ||
|   | 257e1f3096 | ||
|   | 6441ae77d9 | ||
|   | 3f0938072f | ||
|   | 2fe06a28aa | ||
|   | 38750ba798 | ||
|   | b4c01f8905 | ||
|   | 104d70c88e | ||
|   | 133a350f2f | ||
|   | 496ab55357 | ||
|   | a391758e31 | ||
|   | eaf8ad4949 | ||
|   | 2f583bdcf3 | ||
|   | db19012a41 | ||
|   | 62be46884e | ||
|   | 3f2e6a48a9 | ||
|   | ff124e5f74 | ||
|   | c07421c195 | ||
|   | de8348d3b9 | ||
|   | 184df8a853 | ||
|   | 12768a147c | ||
|   | 94161cea37 | ||
|   | 52a4ef7cf7 | ||
|   | 3432f71500 | ||
|   | 8282aa6c24 | ||
|   | 59f9eaa1c9 | ||
|   | 4433b735c4 | ||
|   | 9cd30fa6b5 | ||
|   | 2c7bbfb500 | ||
|   | 51a1d23bf9 | ||
|   | c02e6e82bc | ||
|   | e37a3fa7e6 | ||
|   | e23c2ffecc | ||
|   | 744f11d045 | ||
|   | b7eb1f3e8b | ||
|   | 3f28091e52 | ||
|   | b4ee86955d | ||
|   | 8b866efe92 | ||
|   | fb61ab8df7 | ||
|   | 990956ece7 | ||
|   | 414b592d53 | ||
|   | 751154d9da | ||
|   | 29addc499c | ||
|   | caa98de581 | ||
|   | 1fd1ec4312 | ||
|   | f4607626e4 | ||
|   | b494c15e4b | ||
|   | 3c0defa125 | ||
|   | f2df4d95de | ||
|   | fabcf20e06 | ||
|   | b9510b9ab7 | ||
|   | 52a2ebad04 | ||
|   | 89a2dc71fc | ||
|   | adb0de43d8 | ||
|   | e2a811a720 | ||
|   | 5bcafc5c17 | ||
|   | 2d24536caf | ||
|   | b088c4086b | ||
|   | 3c58f4abd3 | ||
|   | e2a9b27b2b | ||
|   | fcd5f06c09 | ||
|   | 6d93c8b3fd | ||
|   | 2663e1be5d | ||
|   | 0fa6be4614 | ||
|   | 46163a6607 | ||
|   | 645ef093f7 | ||
|   | 7551e134da | ||
|   | 5bec5fb6cb | ||
|   | c176af4da5 | ||
|   | 2631f03108 | ||
|   | 525c71658b | ||
|   | 10e5778deb | ||
|   | 6512a5fd6b | ||
|   | 1af40b1345 | ||
|   | 0418b68051 | ||
|   | a7283864e8 | ||
|   | 4950bad2a7 | ||
|   | 470ac0eae3 | ||
|   | 87abbf9b20 | ||
|   | 3e7e88cd5f | ||
|   | b7e1539699 | ||
|   | 8492f2ba24 | ||
|   | 737f4eb1c1 | ||
|   | 58191ea66b | ||
|   | 1f786df462 | ||
|   | fa4c481aed | ||
|   | d555fd7883 | ||
|   | fe7ece1f5a | ||
|   | 2bb3aed729 | ||
|   | 488a42696c | ||
|   | f43ff45683 | ||
|   | bde1451896 | ||
|   | fff2ca6f26 | ||
|   | bec57a6cee | ||
|   | a012ca4fac | ||
|   | 3ba49b0a50 | ||
|   | 314aa024b5 | ||
|   | 598f750859 | ||
|   | 8057848458 | ||
|   | e80c28a530 | ||
|   | 5d05b66902 | ||
|   | 59634b2cf5 | ||
|   | 7c3892f5a2 | ||
|   | 19406a238b | ||
|   | d6146197dd | ||
|   | 38f241479c | ||
|   | aa45999824 | ||
|   | 3b7593ed7f | ||
|   | f959cafb36 | ||
|   | e92d204d42 | ||
|   | f543161234 | ||
|   | 9cc1017912 | ||
|   | fc719c19f9 | ||
|   | ad97fc6855 | ||
|   | 407dc74502 | ||
|   | e5e764b402 | ||
|   | 65ad65fe52 | ||
|   | 8d09d20510 | ||
|   | 5a5b04b2b0 | ||
|   | 3113bac8e6 | ||
|   | 9217f2c916 | ||
|   | 32a49b7846 | ||
|   | 12ef034b7b | ||
|   | e70e4a21f2 | ||
|   | 7826fb4f04 | ||
|   | 8f1b8909dc | ||
|   | 3f7a989d38 | ||
|   | 4d3fd7598d | ||
|   | 620e3cef20 | ||
|   | 812a61939e | ||
|   | 793c6c2f7b | ||
|   | f8f4d0f646 | ||
|   | 3a92aa751f | ||
|   | 6882273aa0 | ||
|   | 3b0197620f | ||
|   | f6240e114c | ||
|   | 0f3c129b95 | ||
|   | 6f87b01c47 | ||
|   | 32110a9866 | ||
|   | ba459f4d20 | ||
|   | d868e6bfaf | ||
|   | 9f3499a7c3 | ||
|   | ccec7732a7 | ||
|   | 3b980a173f | ||
|   | 246139f90b | ||
|   | 4e85fb7d8d | ||
|   | ab32411b0c | ||
|   | 477f28a6bd | ||
|   | 96ef0a178d | ||
|   | ab603e7ef7 | ||
|   | d52104a62a | ||
|   | 8d8d1cfdd6 | ||
|   | 5451751513 | ||
|   | 92ae26bb9f | ||
|   | 3c8ee0c8cb | ||
|   | 20f76b8118 | ||
|   | d8eeeead18 | ||
|   | 5452162bc3 | ||
|   | a4adcba405 | ||
|   | 66da594382 | ||
|   | aa426842f2 | ||
|   | ed53a45228 | ||
|   | 2ddbcb2369 | ||
|   | 61a58ff3c9 | ||
|   | 638aee65c0 | ||
|   | f21c49f8da | ||
|   | 583d2cb4e4 | ||
|   | aa70dcfc8f | ||
|   | ffb61c425b | ||
|   | 858cf5e0c9 | ||
|   | 881dd4666e | ||
|   | c4aeaf7fe8 | ||
|   | ea1f5a8fc6 | ||
|   | 7f1e420a0a | ||
|   | 1272eaf07f | ||
|   | 2fe760cc4b | ||
|   | df3a50bae8 | ||
|   | 0db3605f33 | ||
|   | 173fa92116 | ||
|   | 02ca58c1eb | ||
|   | 4a22fe58bf | ||
|   | 3fc478a14b | ||
|   | 203bc674fe | ||
|   | a4a9f0a04c | ||
|   | 918e7fffeb | ||
|   | e1648de661 | ||
|   | f7c94e6343 | ||
|   | 2fba8e29e0 | ||
|   | de1bb4e203 | ||
|   | b4680a5c25 | ||
|   | d44f40d105 | ||
|   | 8d5771e302 | ||
|   | fb31f99aed | ||
|   | b97f3a9ecf | ||
|   | ac22172a6e | ||
|   | 57367380f5 | ||
|   | 7101cc3170 | ||
|   | 7051411be7 | ||
|   | bb8397b9b1 | ||
|   | bb8fa61cb4 | ||
|   | 10147ee331 | ||
|   | 3779ac2c8a | ||
|   | bdad4db9ec | ||
|   | 36c69124f7 | ||
|   | 7c8c811134 | ||
|   | ccfc9f3ab0 | ||
|   | d163b92e0b | ||
|   | 887a21afb9 | ||
|   | 634adc9f71 | ||
|   | 974f01cef7 | ||
|   | ef9ade3548 | ||
|   | 24fdb73b44 | ||
|   | ba06a87ba8 | ||
|   | e6be483755 | ||
|   | a36bfced47 | ||
|   | 4faf421d5a | ||
|   | a739455414 | ||
|   | 73f6e75d8d | ||
|   | 17e7f8057a | ||
|   | b62c157680 | ||
|   | 816f5162f9 | ||
|   | 9d8f8277aa | ||
|   | cca14053a4 | ||
|   | 427790f005 | ||
|   | 17845bf71e | ||
|   | 452f5ab3ba | ||
|   | 335744e78a | ||
|   | c9e24439b2 | ||
|   | 61c697b6db | ||
|   | 3227d4f3ed | ||
|   | 7e27afb645 | ||
|   | 9ba4790b4d | ||
|   | 3a26f7f4d5 | ||
|   | 8b99617513 | ||
|   | 587655f063 | ||
|   | 7e9ecf4eb2 | ||
|   | 5413010c60 | ||
|   | 7d13cf1587 | ||
|   | 24cd13935a | ||
|   | 1ae7dbec67 | ||
|   | 2b0a2ab3bc | ||
|   | 4ed0f3e5f0 | ||
|   | 9cacc703dd | ||
|   | 9ae70c6519 | ||
|   | 02b38fed49 | ||
|   | b2a65f809f | ||
|   | 2931869522 | ||
|   | 11d8640ba6 | ||
|   | 65ff947b5e | ||
|   | 2dd7db4808 | ||
|   | 1d7354696e | ||
|   | cbceac4c8a | ||
|   | 297877fbe2 | ||
|   | 0d92451c49 | ||
|   | c8a58dcb69 | ||
|   | a4dea25d76 | ||
|   | bfb0235fc6 | ||
|   | 6dcc3d637f | ||
|   | 9bb4d17e31 | ||
|   | 9df09db5fe | ||
|   | d8e28ec274 | ||
|   | d3905734c1 | ||
|   | 8fe7f923ec | ||
|   | 933f38390b | ||
|   | a4e019442f | ||
|   | d1c4e60636 | ||
|   | 765d0228c0 | ||
|   | 2d2020a20d | ||
|   | 03ab282f67 | ||
|   | 7f94cb1cad | ||
|   | a2a303bd72 | ||
|   | d34bf9a14d | ||
|   | 68faba6bde | ||
|   | 5c5b9cfd96 | ||
|   | 9d683f4767 | ||
|   | f2912bad95 | ||
|   | c8adfe0131 | ||
|   | 8b7e637e74 | ||
|   | 43cffd7c4a | ||
|   | f3dad3765e | ||
|   | 70c25141fc | ||
|   | b1b81a2672 | ||
|   | 46197bf262 | ||
|   | 58ec409e7f | ||
|   | c2d68599de | ||
|   | 65f00f3af2 | ||
|   | 6544326ffd | ||
|   | a23c206ccb | ||
|   | 1b152e6bd0 | ||
|   | d9624d9882 | ||
|   | 178b8471cc | ||
|   | 719d2092a7 | ||
|   | 2f3a4675da | ||
|   | 9513be664b | ||
|   | 64d8b7853a | ||
|   | 4174e57c13 | ||
|   | 88b395599a | ||
|   | b6d682c92c | ||
|   | 3b02894341 | ||
|   | f3feb13dfe | ||
|   | 114d8d0aba | ||
|   | 503a843bb3 | ||
|   | c4d91aff40 | ||
|   | 2ffe5faf6e | ||
|   | 5d2a03aa82 | ||
|   | f4c83d1221 | ||
|   | 100e91714b | ||
|   | dafb7b5259 | ||
|   | 92906e217c | ||
|   | 5c7b721879 | ||
|   | c27dcb0414 | ||
|   | 7fcae1e974 | ||
|   | cc4659f5c6 | ||
|   | 9fce12d6b4 | ||
|   | deb2f30b37 | ||
|   | 751a3f0e94 | ||
|   | fee2a07e08 | ||
|   | 17726abb0a | ||
|   | 01f740ce69 | ||
|   | b168ccb605 | ||
|   | 04a31a52ae | ||
|   | 6a236fb91e | ||
|   | 619fa1bff8 | ||
|   | 53b37e8d0c | ||
|   | f8eb8adfbe | ||
|   | 3c2aecb81f | ||
|   | efca9e11d6 | ||
|   | 49189e0e43 | ||
|   | ea86c9bafb | ||
|   | c361b6a85c | ||
|   | c7ff45045c | ||
|   | 090af35ea1 | ||
|   | 8e668ca633 | ||
|   | 4d9a16f33b | ||
|   | c9f2a0f694 | ||
|   | 46bac3069e | ||
|   | d21aa0d85f | ||
|   | 3e87d699eb | ||
|   | 23aa216908 | ||
|   | e34e681157 | ||
|   | acd543fe4b | ||
|   | c2df5939d6 | ||
|   | b52e74b615 | ||
|   | ec2bb039ae | ||
|   | aeb9f5775f | ||
|   | 47a20756b9 | ||
|   | a7aba1d585 | ||
|   | c89af0cea4 | ||
|   | 9d3a109946 | ||
|   | 079f1e6fff | ||
|   | 7249b11899 | ||
|   | 4cfb000812 | ||
|   | 5262a41619 | ||
|   | 887590730d | ||
|   | adb49bdf0b | ||
|   | 7f3aadc157 | ||
|   | eb1c85f3f5 | ||
|   | dbdc884c96 | ||
|   | 5166354e34 | ||
|   | 04ea95049a | ||
|   | ec62e49001 | ||
|   | 6fb21850d8 | ||
|   | 98b50fd942 | ||
|   | 729c962b7c | ||
|   | ebc15e60a8 | ||
|   | 85f2d94253 | ||
|   | 981a536cb5 | ||
|   | abf7c333b1 | ||
|   | b2f547e934 | ||
|   | 0870a25e2f | ||
|   | 1091e577a5 | ||
|   | 151ad16fe6 | ||
|   | e325258091 | ||
|   | 1139a02b40 | ||
|   | 4b90953226 | ||
|   | d77fc01580 | ||
|   | 216d84faeb | ||
|   | 0c9d95f183 | ||
|   | 913739d732 | ||
|   | 7ecb5af587 | ||
|   | 87f0e79749 | ||
|   | c85145d73c | ||
|   | eea689841b | ||
|   | e50a59361d | ||
|   | 9862185bda | ||
|   | fe05d35bbb | ||
|   | ba602c17d4 | ||
|   | 831bd07b0d | ||
|   | 175c5d9fc3 | 
							
								
								
									
										41
									
								
								COPYING
									
									
									
									
									
								
							
							
						
						
									
										41
									
								
								COPYING
									
									
									
									
									
								
							| @@ -1,12 +1,12 @@ | ||||
| 		    GNU GENERAL PUBLIC LICENSE | ||||
| 		       Version 2, June 1991 | ||||
|                     GNU GENERAL PUBLIC LICENSE | ||||
|                        Version 2, June 1991 | ||||
|  | ||||
|  Copyright (C) 1989, 1991 Free Software Foundation, Inc. | ||||
|      59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  Copyright (C) 1989, 1991 Free Software Foundation, Inc., | ||||
|  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  Everyone is permitted to copy and distribute verbatim copies | ||||
|  of this license document, but changing it is not allowed. | ||||
|  | ||||
| 			    Preamble | ||||
|                             Preamble | ||||
|  | ||||
|   The licenses for most software are designed to take away your | ||||
| freedom to share and change it.  By contrast, the GNU General Public | ||||
| @@ -15,7 +15,7 @@ software--to make sure the software is free for all its users.  This | ||||
| General Public License applies to most of the Free Software | ||||
| Foundation's software and to any other program whose authors commit to | ||||
| using it.  (Some other Free Software Foundation software is covered by | ||||
| the GNU Library General Public License instead.)  You can apply it to | ||||
| the GNU Lesser General Public License instead.)  You can apply it to | ||||
| your programs, too. | ||||
|  | ||||
|   When we speak of free software, we are referring to freedom, not | ||||
| @@ -55,8 +55,8 @@ patent must be licensed for everyone's free use or not licensed at all. | ||||
|  | ||||
|   The precise terms and conditions for copying, distribution and | ||||
| modification follow. | ||||
|  | ||||
| 		    GNU GENERAL PUBLIC LICENSE | ||||
|  | ||||
|                     GNU GENERAL PUBLIC LICENSE | ||||
|    TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION | ||||
|  | ||||
|   0. This License applies to any program or other work which contains | ||||
| @@ -110,7 +110,7 @@ above, provided that you also meet all of these conditions: | ||||
|     License.  (Exception: if the Program itself is interactive but | ||||
|     does not normally print such an announcement, your work based on | ||||
|     the Program is not required to print an announcement.) | ||||
|  | ||||
|  | ||||
| These requirements apply to the modified work as a whole.  If | ||||
| identifiable sections of that work are not derived from the Program, | ||||
| and can be reasonably considered independent and separate works in | ||||
| @@ -168,7 +168,7 @@ access to copy from a designated place, then offering equivalent | ||||
| access to copy the source code from the same place counts as | ||||
| distribution of the source code, even though third parties are not | ||||
| compelled to copy the source along with the object code. | ||||
|  | ||||
|  | ||||
|   4. You may not copy, modify, sublicense, or distribute the Program | ||||
| except as expressly provided under this License.  Any attempt | ||||
| otherwise to copy, modify, sublicense or distribute the Program is | ||||
| @@ -225,7 +225,7 @@ impose that choice. | ||||
|  | ||||
| This section is intended to make thoroughly clear what is believed to | ||||
| be a consequence of the rest of this License. | ||||
|  | ||||
|  | ||||
|   8. If the distribution and/or use of the Program is restricted in | ||||
| certain countries either by patents or by copyrighted interfaces, the | ||||
| original copyright holder who places the Program under this License | ||||
| @@ -255,7 +255,7 @@ make exceptions for this.  Our decision will be guided by the two goals | ||||
| of preserving the free status of all derivatives of our free software and | ||||
| of promoting the sharing and reuse of software generally. | ||||
|  | ||||
| 			    NO WARRANTY | ||||
|                             NO WARRANTY | ||||
|  | ||||
|   11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY | ||||
| FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN | ||||
| @@ -277,9 +277,9 @@ YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER | ||||
| PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE | ||||
| POSSIBILITY OF SUCH DAMAGES. | ||||
|  | ||||
| 		     END OF TERMS AND CONDITIONS | ||||
|  | ||||
| 	    How to Apply These Terms to Your New Programs | ||||
|                      END OF TERMS AND CONDITIONS | ||||
|  | ||||
|             How to Apply These Terms to Your New Programs | ||||
|  | ||||
|   If you develop a new program, and you want it to be of the greatest | ||||
| possible use to the public, the best way to achieve this is to make it | ||||
| @@ -303,17 +303,16 @@ the "copyright" line and a pointer to where the full notice is found. | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|     GNU General Public License for more details. | ||||
|  | ||||
|     You should have received a copy of the GNU General Public License | ||||
|     along with this program; if not, write to the Free Software | ||||
|     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | ||||
|  | ||||
|     You should have received a copy of the GNU General Public License along | ||||
|     with this program; if not, write to the Free Software Foundation, Inc., | ||||
|     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. | ||||
|  | ||||
| Also add information on how to contact you by electronic and paper mail. | ||||
|  | ||||
| If the program is interactive, make it output a short notice like this | ||||
| when it starts in an interactive mode: | ||||
|  | ||||
|     Gnomovision version 69, Copyright (C) year  name of author | ||||
|     Gnomovision version 69, Copyright (C) year name of author | ||||
|     Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. | ||||
|     This is free software, and you are welcome to redistribute it | ||||
|     under certain conditions; type `show c' for details. | ||||
| @@ -336,5 +335,5 @@ necessary.  Here is a sample; alter the names: | ||||
| This General Public License does not permit incorporating your program into | ||||
| proprietary programs.  If your program is a subroutine library, you may | ||||
| consider it more useful to permit linking proprietary applications with the | ||||
| library.  If this is what you want to do, use the GNU Library General | ||||
| library.  If this is what you want to do, use the GNU Lesser General | ||||
| Public License instead of this License. | ||||
|   | ||||
							
								
								
									
										235
									
								
								NEWS
									
									
									
									
									
								
							
							
						
						
									
										235
									
								
								NEWS
									
									
									
									
									
								
							| @@ -1,3 +1,238 @@ | ||||
| 3.12.2 | ||||
| ====== | ||||
| * Fix turning off airplane mode [Giovanni; #728512] | ||||
| * Handle empty VPN keyfiles [Adel; #728681] | ||||
| * Fix setting zero-level in osdWindow [Bastien; #727384] | ||||
| * Fix removal of multiple workspace thumbnails at once [Florian; #728820] | ||||
| * Make airplane mode menu insensitive in lock screen [Giovanni; #729224] | ||||
| * Fix keynav for alternatives in AltSwitcher [Florian; #727259] | ||||
| * Fix zombie search providers showing up [Jasper; #728597] | ||||
|  | ||||
| Contributors: | ||||
|   Giovanni Campagna, Adel Gadllah, Florian Müllner, Bastien Nocera, | ||||
|   Jasper St. Pierre | ||||
|  | ||||
| Translations: | ||||
|   Wouter Bolsterlee [nl], Daniel Korostil [uk], Ihar Hrachyshka [be], | ||||
|   Giovanni Campagna [it], Carles Ferrando [ca@valencia] | ||||
|  | ||||
| 3.12.1 | ||||
| ====== | ||||
| * Ensure the currently focused app icon is viewable [Rui; #726759] | ||||
| * Improve language in location menu [Zeeshan; #726498] | ||||
| * Improve HiDpi support [Cosimo; #726907] | ||||
| * Set accessible role for window previews [Alejandro; #726670] | ||||
| * Fix bad antialiasing on panel menu buttons [Carlos; #727336] | ||||
| * Don't hide location menu [Zeeshan; #727398] | ||||
| * Fix IM candidate window obscuring current text [Rui; #727579] | ||||
| * Fix extension-prefs tool when linked with --as-needed [Florian; #727948] | ||||
| * Don't always extend struts to the screen edge [Florian; #663690] | ||||
|  | ||||
| Contributors: | ||||
|   Zeeshan Ali (Khattak), Cosimo Cecchi, Piotr Drąg, Rui Matos, Simon McVittie, | ||||
|   Florian Müllner, Alejandro Piñeiro, Carlos Soriano | ||||
|  | ||||
| Translations: | ||||
|   Khaled Hosny [ar], Piotr Drąg [pl], Yosef Or Boczko [he], | ||||
|   Antonio Fernandes C. Neto [pt_BR], Marek Černocký [cs], maria thukididu [el], | ||||
|   Andika Triwidada [id], Daniel Mustieles [es], Changwoo Ryu [ko], | ||||
|   Benjamin Steinwender [de], Sphinx Jiang [zh_CN], | ||||
|   Inaki Larranaga Murgoitio [eu], Marcus Lundblad [sv], Aurimas Černius [lt], | ||||
|   Stas Solovey [ru], Alexandre Franke [fr], Matej Urbančič [sl], | ||||
|   Fran Diéguez [gl], Pau Iranzo [ca], Luca Ferretti [it], Milo Casagrande [it], | ||||
|   Tiago S [pt], Victor Ibragimov [tg], Dirgita [id], Khoem Sokhem [km], | ||||
|   Rūdolfs Mazurs [lv], Balázs Úr [hu], Ask H. Larsen [da], Ikuya Awashiro [ja], | ||||
|   Мирослав Николић [sr, sr@latin] | ||||
|  | ||||
| 3.12.0 | ||||
| ====== | ||||
| * gdm: Reset greeter when coming back to login screen [Jasper; #726989] | ||||
|  | ||||
| Contributors: | ||||
|   Jasper St. Pierre | ||||
|  | ||||
| Translations: | ||||
|   Daniel Martinez [an], Yuri Myasoedov [ru], Inaki Larranaga Murgoitio [eu], | ||||
|   Abderrahim Kitouni [ar], Praveen Illa [te], Matej Urbančič [sl], | ||||
|   Chao-Hsiung Liao [zh_HK, zh_TW], Frédéric Péters [fr], | ||||
|   Мирослав Николић [sr, sr@latin], Ask H. Larsen [da], Kenneth Nielsen [da], | ||||
|   Jiro Matsuzawa [ja], Dušan Kazik [sk] | ||||
|  | ||||
| 3.11.92 | ||||
| ======= | ||||
| * calendar: Grab key focus after changing day [Volker; #725606] | ||||
| * gdm: Don't load user list if disabled [Florian; #725905] | ||||
| * Don't show network-offline in the top bar [Jasper; #725340] | ||||
| * Improve radial shade effect of modal dialogs [Giovanni; #725830] | ||||
| * Fix broken suspend-on-idle functionality [Giovanni; #712706] | ||||
| * Close wifi selection dialog when device disappears [Giovanni; #723935] | ||||
| * Don't close chats when pressing Escape [Giovanni; #724178] | ||||
| * Improve smartcard support in login/lock screen [Ray; #726262, #726263] | ||||
| * Wake up screen when resuming from suspend [Giovanni; #726378] | ||||
| * Make bluetooth and location items insensitive when locked [Florian; #726319] | ||||
| * Don't show bluetooth icon when there is no adapter [Giovanni; #725057] | ||||
| * Make sure to keep the OSK on top of modal dialogs [Rui; #719451] | ||||
| * Misc. bug fixes and cleanups [Giovanni, Ray, Adel, Daniel, Jasper, Florian; | ||||
|   #725832, #725958, #722149, #724977, #724798, #725020, #723976, #726119, | ||||
|   #726238, #585500, #704844, #726323, #726322, #726120, #726414] | ||||
|  | ||||
| Contributors: | ||||
|   Giovanni Campagna, Daniel Drake, Adel Gadllah, Rui Matos, Florian Müllner, | ||||
|   Volker Sobek, Jasper St. Pierre, Ray Strode | ||||
|  | ||||
| Translations: | ||||
|   Fabio Tomat [fur], Rafael Ferreira [pt_BR], Fran Diéguez [gl], | ||||
|   Marek Černocký [cs], Baurzhan Muftakhidinov [kk], Andika Triwidada [id], | ||||
|   A S Alam [pa], Rūdolfs Mazurs [lv], Wylmer Wang [zh_CN], | ||||
|   Aurimas Černius [lt], Cheng-Chia Tseng [zh_TW], Stas Solovey [ru], | ||||
|   Tiagosdot [pt], Benjamin Steinwender [de], Frédéric Peters [fr], | ||||
|   Daniel Korostil [uk], Yaron Shahrabani [he], Ville-Pekka Vainio [fi], | ||||
|   maria thukididu [el], Victor Ibragimov [tg], Kjartan Maraas [nb], | ||||
|   Gábor Kelemen [hu], Ask H. Larsen [da] | ||||
|  | ||||
| 3.11.91 | ||||
| ======= | ||||
| * Don't use network profile name in menu [Giovanni; #725586] | ||||
| * calendar: Make date label clickable to return to current date [Vit; #641366] | ||||
| * Misc. bug fixes [Florian, Zeeshan, Adel, Jasper, Dan, Volker; #724813, | ||||
|   #724686, #725082, #724870, #724779, #725533] | ||||
|  | ||||
| Contributors: | ||||
|   Zeeshan Ali (Khattak), Giovanni Campagna, Piotr Drąg, Adel Gadllah, | ||||
|   Florian Müllner, Volker Sobek, Vit Stanislav, Jasper St. Pierre, Dan Williams | ||||
|  | ||||
| Translations: | ||||
|   Victor Ibragimov [tg], Aurimas Černius [lt], Dimitris Spingos [el], | ||||
|   Andika Triwidada [id], Rafael Ferreira [pt_BR], Daniel Mustieles [es], | ||||
|   Baurzhan Muftakhidinov [kk], Marek Černocký [cs], Ihar Hrachyshka [be], | ||||
|   eternalhui [zh_CN], Yosef Or Boczko [he], Fran Diéguez [gl], | ||||
|   Khaled Hosny [ar], Ville-Pekka Vainio [fi], Piotr Drąg [pl], | ||||
|   Kjartan Maraas [nb], Changwoo Ryu [ko] | ||||
|  | ||||
| 3.11.90 | ||||
| ======= | ||||
| * Stop showing two bluetooth entries [Giovanni; #709353] | ||||
| * Improve styling of login/lock screen [Reda; #723833] | ||||
| * Fix magnifier crosshairs [Magdalen; #723709] | ||||
| * Make NetworkManager support optional [Michael; #669495] | ||||
| * Make middle-click open a new instance [Florian; #695010] | ||||
| * Scale the UI on high resolution displays [Cosimo, Adel; #705410, #724607] | ||||
| * Remove notification counter on screen shield [Carlos; #709275] | ||||
| * Improve app picker transition [Carlos; #722331] | ||||
| * Add geolocation indicator to status menu [Zeeshan; #723684] | ||||
| * Improve timestamps in chat notifications [Carlos; #708031, #715158] | ||||
| * Improve network menus [Giovanni; #723570] | ||||
| * Add "VPN Setting" item to VPN submenu [Giovanni; #709167] | ||||
| * Improve appearance of disclosure arrows [Carlos; #720206] | ||||
| * Add GSetting key to disable version validation of extensions [Adel; #724683] | ||||
| * Delay auto-removing empty workspaces [Florian; #709064] | ||||
| * Offer offline updates in the shutdown dialog [Kalev; #722898] | ||||
| * Animate tile previews [Florian; #665758] | ||||
| * Misc. bug fixes and cleanups [Giovanni, Ryan, Debarshi, Florian; #709128, | ||||
|   #722342, #723661, #724184, #724256, #724293, #724305, #722554, #724282, | ||||
|   #724690, #722928] | ||||
|  | ||||
| Contributors: | ||||
|   Zeeshan Ali (Khattak), Magdalen Berns, Michael Biebl, Giovanni Campagna, | ||||
|   Cosimo Cecchi, Adel Gadllah, Reda Lazri, Kalev Lember, Ryan Lortie, | ||||
|   Florian Müllner, Debarshi Ray, Carlos Soriano, Jasper St. Pierre, | ||||
|   Colin Walters | ||||
|  | ||||
| Translations: | ||||
|   Victor Ibragimov [tg], Daniel Mustieles [es], Khaled Hosny [ar], | ||||
|   Enrico Nicoletto [pt_BR], Yosef Or Boczko [he], Fran Diéguez [gl], | ||||
|   Marek Černocký [cs], Baurzhan Muftakhidinov [kk], Jorge Pérez Pérez [an], | ||||
|   Kjartan Maraas [nb], David Lüder [de], Daniel Korostil [uk], ngoswami [as], | ||||
|   Rafael Ferreira [pt_BR] | ||||
|  | ||||
| 3.11.5 | ||||
| ====== | ||||
| * Fix extension preference tool [Florian; #722334] | ||||
| * Fix keyboard activation of legacy tray icons [Giovanni; #721267] | ||||
| * Add radial background shade for modal dialogs [Giovanni; #669798] | ||||
| * Show attached modal windows in the overview [Giovanni; #650843] | ||||
| * Add support for desktop actions [Giovanni; #669603] | ||||
| * Indicate in system status when location service is used [Zeeshan; #709372] | ||||
| * Add support for extended app folder schema [Jasper; #723179] | ||||
| * Show status icon for wired network connections [Jasper; #708966] | ||||
| * Indicate airplane mode in network selection dialog [Giovanni; #709128] | ||||
| * Misc bug fixes and cleanups [Florian, Sebastian, Giovanni, Tim, Matt, Jasper; | ||||
|   #722417, #722494, #722547, #722593, #722434, #722787, #722690, #722840, | ||||
|   #722660, #722812, #723197, #722927, #723306, #723308, #723523, #709685, | ||||
|   #723570] | ||||
|  | ||||
| Contributors: | ||||
|   Zeeshan Ali (Khattak), Magdalen Berns, Giovanni Campagna, William Jon McCann, | ||||
|   Sebastian Keller, Tim Lunn, Florian Müllner, Carlos Soriano, | ||||
|   Jasper St. Pierre, Rico Tzschichholz, Matt Watson | ||||
|  | ||||
| Translations: | ||||
|   Marek Černocký [cs], Mattias Põldaru [et], Tong Hui [zh_CN], | ||||
|   Victor Ibragimov [tg], Enrico Nicoletto [pt_BR], Daniel Mustieles [es], | ||||
|   Fran Diéguez [gl], Kjartan Maraas [nb], Nilamdyuti Goswami [as], | ||||
|   Aurimas Černius [lt], Stas Solovey [ru], Yosef Or Boczko [he], | ||||
|   Jorge Pérez Pérez [an], Dimitris Spingos [el], Baurzhan Muftakhidinov [kk], | ||||
|   Chao-Hsiung Liao [zh_HK, zh_TW], Shankar Prasad [kn], Yaron Shahrabani [he], | ||||
|   Andika Triwidada [id] | ||||
|  | ||||
| 3.11.4 | ||||
| ====== | ||||
| * Fix removal of workspacaes that are not at the end [Giovanni; #721417] | ||||
| * Allow session mode to be specified in the environment [Ray; #720894] | ||||
| * Special-case launching of terminals [Debarshi; #695010] | ||||
| * Always show arrow if app switcher is scrollable [Jonh; #711467] | ||||
| * Implement new app folders system [Jasper; #722117] | ||||
| * Remove arrow from background menu [Tarun; #699608] | ||||
| * Misc bug fixes and cleanups [Giovanni, Andika, Florian, Ray; #721039, | ||||
|   #721439, #721507, #721629, #721868, #722210] | ||||
|  | ||||
| Contributors: | ||||
|   Giovanni Campagna, Piotr Drąg, Tarun Kumar Joshi, Florian Müllner, | ||||
|   Debarshi Ray, Jasper St. Pierre, Ray Strode, Andika Triwidada, Jonh Wendell | ||||
|  | ||||
| Translations: | ||||
|   Dušan Kazik [sk], Tong Hui [zh_CN], Benjamin Steinwender [de], | ||||
|   Matej Urbančič [sl], Jorge Pérez Pérez [an], Kjartan Maraas [nb], | ||||
|   Milo Casagrande [it], Rafael Ferreira [pt_BR], Marek Černocký [cs], | ||||
|   Daniel Mustieles [es], Adorilson Bezerra [pt_BR], Christian Kirbach [de], | ||||
|   Aurimas Černius [lt], Andika Triwidada [id], Baurzhan Muftakhidinov [kk], | ||||
|   Victor Ibragimov [tg], Yosef Or Boczko [he], Dimitris Spingos [el], | ||||
|   Fran Diéguez [gl] | ||||
|  | ||||
| 3.11.3 | ||||
| ====== | ||||
| * Fix fade effect of desktop icons [Florian; #707671] | ||||
| * Fix issues with background management code [Jasper; #709313] | ||||
| * Use new Glib facilities for application search [Jasper; #711631] | ||||
| * Add focus indication to session menu button [Sebastien; #710539] | ||||
| * Fix hover tracking for StEntries [Jasper; #706749] | ||||
| * Fix reentrancy issue in message tray [Jasper; #711694] | ||||
| * Tone down zoom animation on login/unlock [Jasper; #712362] | ||||
| * Allow specifying monitor for OSD [Carlos; #712664] | ||||
| * Fix resetting prompt on user switch [Ray; #710456] | ||||
| * Stop using gnome-bluetooth-applet [Bastien; #719341] | ||||
| * Add support for EAP-FAST password requests [Dan; #719813] | ||||
| * Fix entry focus of chat notifications [Jasper; #709853] | ||||
| * Make window previews keyboard navigatable [Jasper; #644306] | ||||
| * Fix app switcher order with dialog windows [Florian; #719824] | ||||
| * Allow remote search providers without icons [Debarshi; #719965] | ||||
| * Fix various alignment issues in RTL locales [Yosef; #712638, #712596, | ||||
|   #712594, #712600, #712579] | ||||
| * Misc. bug fixes and cleanups [Jasper, Florian, Giovanni, Dan; #712727, | ||||
|   #712753, #719378, #719730, #719803, #710115, #720017, #719815, #719567, | ||||
|   #720298] | ||||
|  | ||||
| Contributors: | ||||
|   Giovanni Campagna, Carlos Garnacho, Sebastien Lafargue, Tim Lunn, | ||||
|   Florian Müllner, Bastien Nocera, Yosef Or Boczko, Debarshi Ray, | ||||
|   Jasper St. Pierre, Ray Strode, Dan Williams | ||||
|  | ||||
| Translations: | ||||
|   Kjartan Maraas [nb], Reinout van Schouwen [nl], Rafael Ferreira [pt_BR], | ||||
|   Mattias Põldaru [et], Emin Tufan Çetin [tr], Jiri Grönroos [fi], | ||||
|   Khaled Hosny [ar], Fran Diéguez [gl], Victor Ibragimov [tg], | ||||
|   Daniel Mustieles [es] | ||||
|  | ||||
| 3.11.2 | ||||
| ====== | ||||
| * Cache search result display actors [Jasper; #704912] | ||||
|   | ||||
							
								
								
									
										2
									
								
								README
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								README
									
									
									
									
									
								
							| @@ -8,7 +8,7 @@ For more information about GNOME Shell, including instructions on how | ||||
| to build GNOME Shell from source and how to get involved with the project, | ||||
| see: | ||||
|  | ||||
|  http://live.gnome.org/GnomeShell | ||||
|  https://wiki.gnome.org/Projects/GnomeShell | ||||
|  | ||||
| Bugs should be reported at http://bugzilla.gnome.org against the 'gnome-shell' | ||||
| product. | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| #!/bin/bash | ||||
| #!/bin/sh | ||||
| # Run this to generate all the initial makefiles, etc. | ||||
|  | ||||
| srcdir=`dirname $0` | ||||
|   | ||||
| @@ -13,9 +13,7 @@ | ||||
|  * General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||||
|  * 02111-1307, USA. | ||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  * Authors: | ||||
|  *      Jasper St. Pierre <jstpierre@mecheye.net> | ||||
| @@ -43,6 +41,8 @@ | ||||
|  | ||||
| #define PLUGIN_API_VERSION 5 | ||||
|  | ||||
| #define EXTENSION_DISABLE_VERSION_CHECK_KEY "disable-extension-version-validation" | ||||
|  | ||||
| typedef struct { | ||||
|   GDBusProxy *proxy; | ||||
| } PluginData; | ||||
| @@ -833,6 +833,16 @@ plugin_get_shell_version (PluginObject  *obj, | ||||
|   return ret; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| plugin_get_version_validation_enabled (PluginObject  *obj, | ||||
|                                        NPVariant     *result) | ||||
| { | ||||
|   gboolean is_enabled = !g_settings_get_boolean (obj->settings, EXTENSION_DISABLE_VERSION_CHECK_KEY); | ||||
|   BOOLEAN_TO_NPVARIANT(is_enabled, *result); | ||||
|  | ||||
|   return TRUE; | ||||
| } | ||||
|  | ||||
| #define METHODS                                 \ | ||||
|   METHOD (list_extensions)                      \ | ||||
|   METHOD (get_info)                             \ | ||||
| @@ -852,6 +862,8 @@ static NPIdentifier api_version_id; | ||||
| static NPIdentifier shell_version_id; | ||||
| static NPIdentifier onextension_changed_id; | ||||
| static NPIdentifier onrestart_id; | ||||
| static NPIdentifier version_validation_enabled_id; | ||||
|  | ||||
|  | ||||
| static bool | ||||
| plugin_object_has_method (NPObject     *npobj, | ||||
| @@ -894,7 +906,8 @@ plugin_object_has_property (NPObject     *npobj, | ||||
|   return (name == onextension_changed_id || | ||||
|           name == onrestart_id || | ||||
|           name == api_version_id || | ||||
|           name == shell_version_id); | ||||
|           name == shell_version_id || | ||||
|           name == version_validation_enabled_id); | ||||
| } | ||||
|  | ||||
| static bool | ||||
| @@ -912,6 +925,8 @@ plugin_object_get_property (NPObject     *npobj, | ||||
|     return plugin_get_api_version (obj, result); | ||||
|   else if (name == shell_version_id) | ||||
|     return plugin_get_shell_version (obj, result); | ||||
|   else if (name == version_validation_enabled_id) | ||||
|     return plugin_get_version_validation_enabled (obj, result); | ||||
|   else if (name == onextension_changed_id) | ||||
|     { | ||||
|       if (obj->listener) | ||||
| @@ -990,6 +1005,7 @@ init_methods_and_properties (void) | ||||
|   /* this is the JS public API; it is manipulated through NPIdentifiers for speed */ | ||||
|   api_version_id = funcs.getstringidentifier ("apiVersion"); | ||||
|   shell_version_id = funcs.getstringidentifier ("shellVersion"); | ||||
|   version_validation_enabled_id = funcs.getstringidentifier ("versionValidationEnabled"); | ||||
|  | ||||
|   get_info_id = funcs.getstringidentifier ("getExtensionInfo"); | ||||
|   list_extensions_id = funcs.getstringidentifier ("listExtensions"); | ||||
|   | ||||
							
								
								
									
										72
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										72
									
								
								configure.ac
									
									
									
									
									
								
							| @@ -1,5 +1,5 @@ | ||||
| AC_PREREQ(2.63) | ||||
| AC_INIT([gnome-shell],[3.11.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) | ||||
| AC_INIT([gnome-shell],[3.12.2],[https://bugzilla.gnome.org/enter_bug.cgi?product=gnome-shell],[gnome-shell]) | ||||
|  | ||||
| AC_CONFIG_HEADERS([config.h]) | ||||
| AC_CONFIG_SRCDIR([src/shell-global.c]) | ||||
| @@ -73,10 +73,10 @@ AS_IF([test x$enable_systemd != xno], [ | ||||
|  | ||||
| AC_MSG_RESULT($enable_systemd) | ||||
|  | ||||
| CLUTTER_MIN_VERSION=1.13.4 | ||||
| CLUTTER_MIN_VERSION=1.15.90 | ||||
| GOBJECT_INTROSPECTION_MIN_VERSION=0.10.1 | ||||
| GJS_MIN_VERSION=1.38.1 | ||||
| MUTTER_MIN_VERSION=3.11.1 | ||||
| GJS_MIN_VERSION=1.39.0 | ||||
| MUTTER_MIN_VERSION=3.12.1 | ||||
| GTK_MIN_VERSION=3.7.9 | ||||
| GIO_MIN_VERSION=2.37.0 | ||||
| LIBECAL_MIN_VERSION=3.5.3 | ||||
| @@ -105,9 +105,7 @@ SHARED_PCS="gio-unix-2.0 >= $GIO_MIN_VERSION | ||||
|             libcanberra libcanberra-gtk3 | ||||
|             telepathy-glib >= $TELEPATHY_GLIB_MIN_VERSION | ||||
|             polkit-agent-1 >= $POLKIT_MIN_VERSION | ||||
|             libnm-glib libnm-util >= $NETWORKMANAGER_MIN_VERSION | ||||
|             libnm-gtk >= $NETWORKMANAGER_MIN_VERSION | ||||
|             libsecret-unstable gcr-base-3 >= $GCR_MIN_VERSION" | ||||
|             gcr-base-3 >= $GCR_MIN_VERSION" | ||||
| if test x$have_systemd = xyes; then | ||||
|   SHARED_PCS="${SHARED_PCS} libsystemd-journal" | ||||
| fi | ||||
| @@ -140,19 +138,11 @@ AS_IF([test x$enable_browser_plugin = xyes], [ | ||||
| ]) | ||||
| AM_CONDITIONAL(BUILD_BROWSER_PLUGIN, test x$enable_browser_plugin = xyes) | ||||
|  | ||||
| AC_MSG_CHECKING([for bluetooth support]) | ||||
| PKG_CHECK_EXISTS([gnome-bluetooth-1.0 >= 3.9.0], | ||||
|         [BLUETOOTH_DIR=`$PKG_CONFIG --variable=applet_libdir gnome-bluetooth-1.0` | ||||
| 	 BLUETOOTH_LIBS=`$PKG_CONFIG --variable=applet_libs gnome-bluetooth-1.0` | ||||
| 	 AC_SUBST([BLUETOOTH_LIBS],["$BLUETOOTH_LIBS"]) | ||||
| 	 AC_SUBST([BLUETOOTH_DIR],["$BLUETOOTH_DIR"]) | ||||
| 	 AC_DEFINE_UNQUOTED([BLUETOOTH_DIR],["$BLUETOOTH_DIR"],[Path to installed GnomeBluetooth typelib and library]) | ||||
| 	 AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet]) | ||||
| 	 AC_SUBST([HAVE_BLUETOOTH],[1]) | ||||
| 	 AC_MSG_RESULT([yes])], | ||||
| PKG_CHECK_MODULES(BLUETOOTH, gnome-bluetooth-1.0 >= 3.9.0, | ||||
|         [AC_DEFINE([HAVE_BLUETOOTH],[1],[Define if you have libgnome-bluetooth-applet]) | ||||
| 	 AC_SUBST([HAVE_BLUETOOTH],[1])], | ||||
| 	[AC_DEFINE([HAVE_BLUETOOTH],[0]) | ||||
| 	 AC_SUBST([HAVE_BLUETOOTH],[0]) | ||||
| 	 AC_MSG_RESULT([no])]) | ||||
| 	 AC_SUBST([HAVE_BLUETOOTH],[0])]) | ||||
|  | ||||
| PKG_CHECK_MODULES(CALENDAR_SERVER, libecal-1.2 >= $LIBECAL_MIN_VERSION libedataserver-1.2 >= $LIBEDATASERVER_MIN_VERSION gio-2.0) | ||||
| AC_SUBST(CALENDAR_SERVER_CFLAGS) | ||||
| @@ -190,6 +180,38 @@ if test "$langinfo_ok" = "yes"; then | ||||
|             [Define if _NL_TIME_FIRST_WEEKDAY is available]) | ||||
| fi | ||||
|  | ||||
| AC_ARG_ENABLE(networkmanager, | ||||
|              AS_HELP_STRING([--disable-networkmanager], | ||||
|                             [disable NetworkManager support  @<:@default=auto@:>@]),, | ||||
|               [enable_networkmanager=auto]) | ||||
|  | ||||
| if test "x$enable_networkmanager" != "xno"; then | ||||
|    PKG_CHECK_MODULES(NETWORKMANAGER, | ||||
|                      [libnm-glib | ||||
|                      libnm-util >= $NETWORKMANAGER_MIN_VERSION | ||||
|                      libnm-gtk >= $NETWORKMANAGER_MIN_VERSION | ||||
|                      libsecret-unstable], | ||||
|                      [have_networkmanager=yes], | ||||
|                      [have_networkmanager=no]) | ||||
|  | ||||
|    GNOME_SHELL_CFLAGS="$GNOME_SHELL_CFLAGS $NETWORKMANAGER_CFLAGS" | ||||
|    GNOME_SHELL_LIBS="$GNOME_SHELL_LIBS $NETWORKMANAGER_LIBS" | ||||
| else | ||||
|    have_networkmanager="no  (disabled)" | ||||
| fi | ||||
|  | ||||
| if test "x$have_networkmanager" = "xyes"; then | ||||
|    AC_DEFINE(HAVE_NETWORKMANAGER, [1], [Define if we have NetworkManager]) | ||||
|    AC_SUBST([HAVE_NETWORKMANAGER], [1]) | ||||
| else | ||||
|    if test "x$enable_networkmanager" = "xyes"; then | ||||
|       AC_MSG_ERROR([Couldn't find NetworkManager.]) | ||||
|    fi | ||||
|    AC_SUBST([HAVE_NETWORKMANAGER], [0]) | ||||
| fi | ||||
|  | ||||
| AM_CONDITIONAL(HAVE_NETWORKMANAGER, test "$have_networkmanager" = "yes") | ||||
|  | ||||
| # Sets GLIB_GENMARSHAL and GLIB_MKENUMS | ||||
| AM_PATH_GLIB_2_0() | ||||
|  | ||||
| @@ -231,3 +253,15 @@ AC_CONFIG_FILES([ | ||||
|   man/Makefile | ||||
| ]) | ||||
| AC_OUTPUT | ||||
|  | ||||
| echo " | ||||
| Build configuration: | ||||
|  | ||||
|        Prefix:                                 ${prefix} | ||||
|        Source code location:                   ${srcdir} | ||||
|        Compiler:                               ${CC} | ||||
|        Compiler Warnings:                      $enable_compile_warnings | ||||
|  | ||||
|        Support for NetworkManager:             $have_networkmanager | ||||
|        Support for GStreamer recording:        $build_recorder | ||||
| " | ||||
|   | ||||
| @@ -39,6 +39,7 @@ dist_theme_DATA =				\ | ||||
| 	theme/filter-selected-rtl.svg		\ | ||||
| 	theme/gnome-shell.css			\ | ||||
| 	theme/logged-in-indicator.svg		\ | ||||
| 	theme/menu-arrow-symbolic.svg		    \ | ||||
| 	theme/message-tray-background.png	\ | ||||
| 	theme/more-results.svg			\ | ||||
| 	theme/noise-texture.png			\ | ||||
|   | ||||
| @@ -2,7 +2,7 @@ | ||||
| Type=Application | ||||
| _Name=GNOME Shell (wayland compositor) | ||||
| _Comment=Window management and application launching | ||||
| Exec=@bindir@/mutter-launch -- gnome-shell-wayland --wayland | ||||
| Exec=@bindir@/mutter-launch -- gnome-shell-wayland --wayland --display-server | ||||
| X-GNOME-Bugzilla-Bugzilla=GNOME | ||||
| X-GNOME-Bugzilla-Product=gnome-shell | ||||
| X-GNOME-Bugzilla-Component=general | ||||
|   | ||||
| @@ -13,12 +13,21 @@ | ||||
|     </key> | ||||
|     <key name="enabled-extensions" type="as"> | ||||
|       <default>[]</default> | ||||
|       <_summary>Uuids of extensions to enable</_summary> | ||||
|       <_summary>UUIDs of extensions to enable</_summary> | ||||
|       <_description> | ||||
|         GNOME Shell extensions have a uuid property; this key lists extensions | ||||
|         GNOME Shell extensions have a UUID property; this key lists extensions | ||||
|         which should be loaded. Any extension that wants to be loaded needs | ||||
|         to be in this list. You can also manipulate this list with the | ||||
|         EnableExtension and DisableExtension DBus methods on org.gnome.Shell. | ||||
|         EnableExtension and DisableExtension D-Bus methods on org.gnome.Shell. | ||||
|       </_description> | ||||
|     </key> | ||||
|     <key name="disable-extension-version-validation" type="b"> | ||||
|       <default>false</default> | ||||
|       <_summary>Disables the validation of extension version compatibility</_summary> | ||||
|       <_description> | ||||
|         GNOME Shell will only load extensions that claim to support the current | ||||
|         running version. Enabling this option will disable this check and try to | ||||
|         load all extensions regardless of the versions they claim to support. | ||||
|       </_description> | ||||
|     </key> | ||||
|     <key name="favorite-apps" type="as"> | ||||
| @@ -29,14 +38,6 @@ | ||||
|         will be displayed in the favorites area. | ||||
|       </_description> | ||||
|     </key> | ||||
|     <key name="app-folder-categories" type="as"> | ||||
|       <default>[ 'Utilities', 'Sundry' ]</default> | ||||
|       <_summary>List of categories that should be displayed as folders</_summary> | ||||
|       <_description> | ||||
|         Each category name in this list will be represented as folder in the | ||||
|         application view, rather than being displayed inline in the main view. | ||||
|       </_description> | ||||
|     </key> | ||||
|     <key name="app-picker-view" type="u"> | ||||
|       <default>0</default> | ||||
|       <summary>App Picker View</summary> | ||||
| @@ -54,10 +55,10 @@ | ||||
|     </key> | ||||
|     <key name="always-show-log-out" type="b"> | ||||
|       <default>false</default> | ||||
|       <_summary>Always show the 'Log out' menuitem in the user menu.</_summary> | ||||
|       <_summary>Always show the 'Log out' menu item in the user menu.</_summary> | ||||
|       <_description> | ||||
|         This key overrides the automatic hiding of the 'Log out' | ||||
|         menuitem in single-user, single-session situations. | ||||
|         menu item in single-user, single-session situations. | ||||
|       </_description> | ||||
|     </key> | ||||
|     <key name="remember-mount-password" type="b"> | ||||
| @@ -73,6 +74,7 @@ | ||||
|     <child name="calendar" schema="org.gnome.shell.calendar"/> | ||||
|     <child name="keybindings" schema="org.gnome.shell.keybindings"/> | ||||
|     <child name="keyboard" schema="org.gnome.shell.keyboard"/> | ||||
|     <child name="location" schema="org.gnome.shell.location"/> | ||||
|   </schema> | ||||
|  | ||||
|   <schema id="org.gnome.shell.calendar" path="/org/gnome/shell/calendar/" | ||||
| @@ -124,6 +126,11 @@ | ||||
|         Keybinding to focus the active notification. | ||||
|       </_description> | ||||
|     </key> | ||||
|     <key name="pause-resume-tweens" type="as"> | ||||
|       <default>[]</default> | ||||
|       <summary>Keybinding that pauses and resumes all running tweens, for debugging purposes</summary> | ||||
|       <description></description> | ||||
|     </key> | ||||
|   </schema> | ||||
|  | ||||
|   <schema id="org.gnome.shell.keyboard" path="/org/gnome/shell/keyboard/" | ||||
| @@ -137,6 +144,32 @@ | ||||
|     </key> | ||||
|   </schema> | ||||
|  | ||||
|   <enum id="org.gnome.shell.geoclue.AccuracyLevel"> | ||||
|     <value value="0" nick="off"/> | ||||
|     <value value="1" nick="country"/> | ||||
|     <value value="4" nick="city"/> | ||||
|     <value value="5" nick="neighborhood"/> | ||||
|     <value value="6" nick="street"/> | ||||
|     <value value="8" nick="exact"/> | ||||
|   </enum> | ||||
|   <schema id="org.gnome.shell.location" | ||||
|           path="/org/gnome/shell/location/" | ||||
|           gettext-domain="@GETTEXT_PACKAGE@"> | ||||
|     <key name="max-accuracy-level" enum="org.gnome.shell.geoclue.AccuracyLevel"> | ||||
|       <default>'exact'</default> | ||||
|       <_summary>The maximum accuracy level of location.</_summary> | ||||
|       <_description> | ||||
|         Configures the maximum level of location accuracy applications are | ||||
|         allowed to see. Valid options are 'off' (disable location tracking), | ||||
|         'country', 'city', 'neighborhood', 'street', and 'exact' (typically | ||||
|         requires GPS receiver). Please keep in mind that this only controls | ||||
|         what GeoClue will allow applications to see and they can find user's | ||||
|         location on their own using network resources (albeit with street-level | ||||
|         accuracy at best). | ||||
|       </_description> | ||||
|     </key> | ||||
|   </schema> | ||||
|  | ||||
|   <schema id="org.gnome.shell.app-switcher" | ||||
|           path="/org/gnome/shell/app-switcher/" | ||||
|           gettext-domain="@GETTEXT_PACKAGE@"> | ||||
|   | ||||
| @@ -157,8 +157,9 @@ StScrollBar StButton#vhandle:active { | ||||
|     min-width: 200px; | ||||
| } | ||||
|  | ||||
| .unicode-arrow { | ||||
|     font-size: 120%; | ||||
| .popup-menu-arrow { | ||||
|     width: 16px; | ||||
|     height: 16px; | ||||
| } | ||||
|  | ||||
| .popup-submenu-menu-item:open { | ||||
| @@ -289,6 +290,20 @@ StScrollBar StButton#vhandle:active { | ||||
|     spacing: 10px; | ||||
| } | ||||
|  | ||||
| .nm-dialog-airplane-box { | ||||
|     spacing: 12px; | ||||
| } | ||||
|  | ||||
| .nm-dialog-airplane-headline { | ||||
|     font-size: 1.1em; | ||||
|     font-weight: bold; | ||||
|     text-align: center; | ||||
| } | ||||
|  | ||||
| .nm-dialog-airplane-text { | ||||
|     color: #999999; | ||||
| } | ||||
|  | ||||
| .nm-dialog-header-icon { | ||||
|     icon-size: 32px; | ||||
| } | ||||
| @@ -655,7 +670,7 @@ StScrollBar StButton#vhandle:active { | ||||
|     color: #e6e6e6; | ||||
|     border-radius: 32px; /* wish we could do 50% */ | ||||
|     padding: 13px; | ||||
|     border: 1px solid #5f5f5f; /* using rgba() is flaky unfortunately */ | ||||
|     border: 2px solid #5f5f5f; /* using rgba() is flaky unfortunately */ | ||||
| } | ||||
|  | ||||
| .system-menu-action:hover, | ||||
| @@ -663,7 +678,7 @@ StScrollBar StButton#vhandle:active { | ||||
|     color: white; | ||||
|     background-color: #4c4c4c; | ||||
|     border: none; | ||||
|     padding: 14px; | ||||
|     padding: 15px; | ||||
| } | ||||
|  | ||||
| .system-menu-action:active { | ||||
| @@ -964,6 +979,8 @@ StScrollBar StButton#vhandle:active { | ||||
|  | ||||
| .app-folder-icon { | ||||
|     padding: 5px; | ||||
|     spacing-rows: 5px; | ||||
|     spacing-columns: 5px; | ||||
| } | ||||
|  | ||||
| .dash-item-container > StButton { | ||||
| @@ -1289,12 +1306,18 @@ StScrollBar StButton#vhandle:active { | ||||
|     font-weight: bold; | ||||
|     text-align: center; | ||||
|     color: #eeeeec; | ||||
|     border-radius: 4px; | ||||
| } | ||||
|  | ||||
| .datemenu-date-label:hover, | ||||
| .datemenu-date-label:focus { | ||||
|     background-color: #999999; | ||||
| } | ||||
|  | ||||
| .datemenu-date-label:active { | ||||
|     background-color: #aaaaaa; | ||||
| } | ||||
|  | ||||
| .calendar-day-base { | ||||
|     font-size: 9pt; | ||||
|     text-align: center; | ||||
| @@ -1453,6 +1476,10 @@ StScrollBar StButton#vhandle:active { | ||||
|     color: #999999; | ||||
| } | ||||
|  | ||||
| .no-networks-box { | ||||
|     spacing: 12px; | ||||
| } | ||||
|  | ||||
| .notification { | ||||
|     border-radius: 10px 10px 0px 0px; | ||||
|     background: rgba(0,0,0,0.9); | ||||
| @@ -1630,8 +1657,8 @@ StScrollBar StButton#vhandle:active { | ||||
|     color: #888888; | ||||
| } | ||||
|  | ||||
| .chat-group-sent, .chat-group-meta { | ||||
|     padding: 8px 0; | ||||
| .chat-empty-line { | ||||
|     font-size: 4px; | ||||
| } | ||||
|  | ||||
| .chat-received { | ||||
| @@ -1656,6 +1683,7 @@ StScrollBar StButton#vhandle:active { | ||||
| .chat-meta-message { | ||||
|     padding-left: 4px; | ||||
|     font-size: 9pt; | ||||
|     font-weight: bold; | ||||
|     color: #bbbbbb; | ||||
| } | ||||
|  | ||||
| @@ -1857,6 +1885,27 @@ StScrollBar StButton#vhandle:active { | ||||
|     border-radius: 8px; | ||||
| } | ||||
|  | ||||
| /* Tile previews */ | ||||
| .tile-preview { | ||||
|     background-color: rgba(74, 144, 217, 0.35); | ||||
|     border: 1px solid #4a90d9; /* Adwaita selected bg color */ | ||||
| } | ||||
|  | ||||
| .tile-preview-left.on-primary { | ||||
|     /* keep in sync with -panel-corner-radius */ | ||||
|     border-radius: 6px 0 0 0; | ||||
| } | ||||
|  | ||||
| .tile-preview-right.on-primary { | ||||
|     /* keep in sync with -panel-corner-radius */ | ||||
|     border-radius: 0 6px 0 0; | ||||
| } | ||||
|  | ||||
| .tile-preview-left.tile-preview-right.on-primary { | ||||
|     /* keep in sync with -panel-corner-radius */ | ||||
|     border-radius: 6px 6px 0 0; | ||||
| } | ||||
|  | ||||
| /* Modal Dialogs */ | ||||
|  | ||||
| /* Dialog Subject Text Style */ | ||||
| @@ -1927,44 +1976,57 @@ StScrollBar StButton#vhandle:active { | ||||
|     padding-top: 20px; | ||||
| } | ||||
|  | ||||
| .end-session-dialog-subject { | ||||
| .end-session-dialog-layout { | ||||
|     padding-left: 17px; | ||||
|     padding-bottom: 20px; | ||||
| } | ||||
|  | ||||
| .end-session-dialog-subject:rtl { | ||||
|     padding-left: 0px; | ||||
| .end-session-dialog-layout:rtl { | ||||
|     padding-right: 17px; | ||||
| } | ||||
|  | ||||
| .end-session-dialog-description { | ||||
|     padding-left: 17px; | ||||
|     width: 28em; | ||||
|     padding-bottom: 10px; | ||||
| } | ||||
|  | ||||
| .end-session-dialog-description:rtl { | ||||
|     padding-right: 17px; | ||||
|     width: 28em; | ||||
|     padding-bottom: 10px; | ||||
|     text-align: right; | ||||
| } | ||||
|  | ||||
| .end-session-dialog-warning { | ||||
|     width: 28em; | ||||
|     color: #f57900; | ||||
|     padding-top: 6px; | ||||
| } | ||||
|  | ||||
| .end-session-dialog-warning:rtl { | ||||
|     width: 28em; | ||||
|     color: #f57900; | ||||
|     padding-top: 6px; | ||||
|     text-align: right; | ||||
| } | ||||
|  | ||||
| .end-session-dialog-logout-icon { | ||||
|     border: 2px solid #8b8b8b; | ||||
|     border-radius: 5px; | ||||
|     width: 32px; | ||||
|     height: 32px; | ||||
|     width: 48px; | ||||
|     height: 48px; | ||||
|     background-size: contain; | ||||
| } | ||||
|  | ||||
| .end-session-dialog-shutdown-icon { | ||||
|     color: #bebebe; | ||||
|     width: 32px; | ||||
|     height: 32px; | ||||
|     width: 48px; | ||||
|     height: 48px; | ||||
| } | ||||
|  | ||||
| .end-session-dialog-inhibitor-layout { | ||||
|     spacing: 16px; | ||||
|     max-height: 200px; | ||||
|     padding-right: 50px; | ||||
|     padding-left: 50px; | ||||
|     padding-right: 65px; | ||||
|     padding-left: 65px; | ||||
| } | ||||
|  | ||||
| .end-session-dialog-session-list, | ||||
| @@ -1976,6 +2038,10 @@ StScrollBar StButton#vhandle:active { | ||||
|     font-weight: bold; | ||||
| } | ||||
|  | ||||
| .end-session-dialog-list-header:rtl { | ||||
|     text-align: right; | ||||
| } | ||||
|  | ||||
| .end-session-dialog-app-list-item, | ||||
| .end-session-dialog-session-list-item { | ||||
|     spacing: 1em; | ||||
| @@ -2095,6 +2161,10 @@ StScrollBar StButton#vhandle:active { | ||||
|     color: #666666; | ||||
| } | ||||
|  | ||||
| .prompt-dialog-description:rtl { | ||||
|     text-align: right; | ||||
| } | ||||
|  | ||||
| .prompt-dialog-password-box { | ||||
|     spacing: 1em; | ||||
|     padding-bottom: 1em; | ||||
| @@ -2321,6 +2391,8 @@ StScrollBar StButton#vhandle:active { | ||||
| .login-dialog-user-list-item { | ||||
|     border-radius: 5px; | ||||
|     padding: .2em; | ||||
|     color: #bfbfbf; | ||||
|     text-shadow: black 0px 2px 2px; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item:ltr { | ||||
| @@ -2331,24 +2403,6 @@ StScrollBar StButton#vhandle:active { | ||||
|     padding-left: 1em; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item .login-dialog-user-list-item-name { | ||||
|     font-size: 20px; | ||||
|     padding-left: 18px; | ||||
|     font-weight: bold; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list:expanded .login-dialog-user-list-item { | ||||
|     color: #bfbfbf; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item, | ||||
| .login-dialog-user-list-item:hover .login-dialog-user-list-item-name, | ||||
| .login-dialog-user-list:expanded .login-dialog-user-list-item:focus .login-dialog-user-list-item-name, | ||||
| .login-dialog-user-list:expanded .login-dialog-user-list-item:logged-in { | ||||
|     color: #bfbfbf; | ||||
|     text-shadow: black 0px 2px 2px; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item:hover { | ||||
|     background-color: rgba(255,255,255,0.1); | ||||
| } | ||||
| @@ -2375,13 +2429,6 @@ StScrollBar StButton#vhandle:active { | ||||
|     background-color: #8b8b8b; | ||||
| } | ||||
|  | ||||
| .login-dialog-user-list-item-icon { | ||||
|     border: 2px solid #8b8b8b; | ||||
|     border-radius: 3px; | ||||
|     width: 64px; | ||||
|     height: 64px; | ||||
| } | ||||
|  | ||||
| .login-dialog-not-listed-label { | ||||
|     font-size: 10.5pt; | ||||
|     font-weight: bold; | ||||
| @@ -2402,12 +2449,13 @@ StScrollBar StButton#vhandle:active { | ||||
|     color: #E8E8E8; | ||||
| } | ||||
|  | ||||
| .login-dialog-username { | ||||
| .login-dialog-username, | ||||
| .user-widget-label { | ||||
|     font-size: 16pt; | ||||
|     font-weight: bold; | ||||
|     text-align: left; | ||||
|     padding-left: 15px; | ||||
|     text-shadow: black 0px 4px 3px 0px; | ||||
|     text-shadow: rgba(0, 0, 0, 0.5) 0px 2px 1px 0px; | ||||
| } | ||||
|  | ||||
| .login-dialog-prompt-layout { | ||||
| @@ -2432,6 +2480,7 @@ StScrollBar StButton#vhandle:active { | ||||
| } | ||||
|  | ||||
| .login-dialog-session-list-button:hover, | ||||
| .login-dialog-session-list-button:focus, | ||||
| .login-dialog-session-list-button:active { | ||||
|     color: white; | ||||
| } | ||||
| @@ -2498,12 +2547,14 @@ StScrollBar StButton#vhandle:active { | ||||
| } | ||||
|  | ||||
| .user-widget-label { | ||||
|     font-size: 20px; | ||||
|     font-weight: bold; | ||||
|     text-align: left; | ||||
| } | ||||
|  | ||||
| .user-widget-label:ltr { | ||||
|     padding-left: 18px; | ||||
|     color:white; | ||||
|     text-shadow: black 0px 4px 3px 0px; | ||||
| } | ||||
|  | ||||
| .user-widget-label:rtl { | ||||
|     padding-right: 18px; | ||||
| } | ||||
|  | ||||
| /* Screen shield */ | ||||
| @@ -2630,4 +2681,5 @@ StScrollBar StButton#vhandle:active { | ||||
|  | ||||
| .background-menu { | ||||
|     -boxpointer-gap: 4px; | ||||
|     -arrow-rise: 0px; | ||||
| } | ||||
|   | ||||
							
								
								
									
										90
									
								
								data/theme/menu-arrow-symbolic.svg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								data/theme/menu-arrow-symbolic.svg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,90 @@ | ||||
| <?xml version="1.0" encoding="UTF-8" standalone="no"?> | ||||
| <!-- Created with Inkscape (http://www.inkscape.org/) --> | ||||
|  | ||||
| <svg | ||||
|    xmlns:dc="http://purl.org/dc/elements/1.1/" | ||||
|    xmlns:cc="http://creativecommons.org/ns#" | ||||
|    xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" | ||||
|    xmlns:svg="http://www.w3.org/2000/svg" | ||||
|    xmlns="http://www.w3.org/2000/svg" | ||||
|    xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" | ||||
|    xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" | ||||
|    width="16" | ||||
|    height="16" | ||||
|    id="svg3863" | ||||
|    version="1.1" | ||||
|    inkscape:version="0.48.4 r9939" | ||||
|    sodipodi:docname="menu-arrow.svg"> | ||||
|   <defs | ||||
|      id="defs3865" /> | ||||
|   <sodipodi:namedview | ||||
|      id="base" | ||||
|      pagecolor="#ffffff" | ||||
|      bordercolor="#666666" | ||||
|      borderopacity="1.0" | ||||
|      inkscape:pageopacity="0.0" | ||||
|      inkscape:pageshadow="2" | ||||
|      inkscape:zoom="15.836083" | ||||
|      inkscape:cx="-3.1641676" | ||||
|      inkscape:cy="11.823817" | ||||
|      inkscape:current-layer="layer1" | ||||
|      showgrid="true" | ||||
|      inkscape:grid-bbox="true" | ||||
|      inkscape:document-units="px" | ||||
|      showguides="true" | ||||
|      inkscape:guide-bbox="true" | ||||
|      inkscape:window-width="1366" | ||||
|      inkscape:window-height="702" | ||||
|      inkscape:window-x="0" | ||||
|      inkscape:window-y="27" | ||||
|      inkscape:window-maximized="1" | ||||
|      inkscape:snap-bbox="true"> | ||||
|     <sodipodi:guide | ||||
|        orientation="1,0" | ||||
|        position="15.996443,16.922964" | ||||
|        id="guide3873" /> | ||||
|     <sodipodi:guide | ||||
|        orientation="0,1" | ||||
|        position="28.041217,3.1256134" | ||||
|        id="guide3875" /> | ||||
|     <sodipodi:guide | ||||
|        orientation="0,1" | ||||
|        position="-0.80372916,24.469088" | ||||
|        id="guide3877" /> | ||||
|     <sodipodi:guide | ||||
|        orientation="1,0" | ||||
|        position="3.0363102,34.649657" | ||||
|        id="guide3879" /> | ||||
|     <sodipodi:guide | ||||
|        orientation="1,0" | ||||
|        position="29.023553,28.577037" | ||||
|        id="guide3881" /> | ||||
|     <inkscape:grid | ||||
|        type="xygrid" | ||||
|        id="grid2988" /> | ||||
|   </sodipodi:namedview> | ||||
|   <metadata | ||||
|      id="metadata3868"> | ||||
|     <rdf:RDF> | ||||
|       <cc:Work | ||||
|          rdf:about=""> | ||||
|         <dc:format>image/svg+xml</dc:format> | ||||
|         <dc:type | ||||
|            rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> | ||||
|         <dc:title></dc:title> | ||||
|       </cc:Work> | ||||
|     </rdf:RDF> | ||||
|   </metadata> | ||||
|   <g | ||||
|      id="layer1" | ||||
|      inkscape:label="Layer 1" | ||||
|      inkscape:groupmode="layer" | ||||
|      transform="translate(0,-16)"> | ||||
|     <path | ||||
|        style="fill:#ffffff;fill-opacity:1;stroke:none" | ||||
|        d="m 4,23 8,0 -4,5 z" | ||||
|        id="path3883" | ||||
|        inkscape:connector-curvature="0" | ||||
|        sodipodi:nodetypes="cccc" /> | ||||
|   </g> | ||||
| </svg> | ||||
| After Width: | Height: | Size: 2.5 KiB | 
| @@ -112,7 +112,7 @@ expand_content_files= | ||||
| # e.g. GTKDOC_CFLAGS=-I$(top_srcdir) -I$(top_builddir) $(GTK_DEBUG_FLAGS) | ||||
| # e.g. GTKDOC_LIBS=$(top_builddir)/gtk/$(gtktargetlib) | ||||
| GTKDOC_CFLAGS=$(GNOME_SHELL_CFLAGS) | ||||
| GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(BLUETOOTH_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la | ||||
| GTKDOC_LIBS=$(GNOME_SHELL_LIBS) $(top_builddir)/src/libgnome-shell-menu.la $(top_builddir)/src/libgnome-shell-base.la $(top_builddir)/src/libgnome-shell.la | ||||
|  | ||||
| # This includes the standard gtk-doc make rules, copied by gtkdocize. | ||||
| include $(top_srcdir)/gtk-doc.make | ||||
|   | ||||
| @@ -17,17 +17,15 @@ packages. If you are interested in building GNOME Shell from source, | ||||
| we would recommend building from version control using the build | ||||
| script described at: | ||||
|  | ||||
|  http://live.gnome.org/GnomeShell | ||||
|  https://wiki.gnome.org/Projects/GnomeShell | ||||
|  | ||||
| Not only will that give you the very latest version of this rapidly | ||||
| changing project, it will be much easier than get GNOME Shell and | ||||
| its dependencies to build from tarballs.</description> | ||||
|   <!-- | ||||
|   <homepage rdf:resource="http://live.gnome.org/GnomeShell" /> | ||||
|   --> | ||||
|   <homepage rdf:resource="https://wiki.gnome.org/Projects/GnomeShell" /> | ||||
|   <mailing-list rdf:resource="http://mail.gnome.org/mailman/listinfo/gnome-shell-list" /> | ||||
|   <download-page rdf:resource="http://download.gnome.org/sources/gnome-shell/" /> | ||||
|   <bug-database rdf:resource="http://bugzilla.gnome.org/browse.cgi?product=gnome-shell" /> | ||||
|   <bug-database rdf:resource="https://bugzilla.gnome.org/browse.cgi?product=gnome-shell" /> | ||||
|  | ||||
|   <category rdf:resource="http://api.gnome.org/doap-extensions#desktop" /> | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,7 @@ misc/config.js: misc/config.js.in Makefile | ||||
| 	sed -e "s|[@]PACKAGE_NAME@|$(PACKAGE_NAME)|g" \ | ||||
| 	    -e "s|[@]PACKAGE_VERSION@|$(PACKAGE_VERSION)|g" \ | ||||
| 	    -e "s|[@]HAVE_BLUETOOTH@|$(HAVE_BLUETOOTH)|g" \ | ||||
| 	    -e "s|[@]HAVE_NETWORKMANAGER@|$(HAVE_NETWORKMANAGER)|g" \ | ||||
| 	    -e "s|[@]GETTEXT_PACKAGE@|$(GETTEXT_PACKAGE)|g" \ | ||||
| 	    -e "s|[@]datadir@|$(datadir)|g" \ | ||||
| 	    -e "s|[@]libexecdir@|$(libexecdir)|g" \ | ||||
|   | ||||
| @@ -80,6 +80,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|                                if (event.get_key_symbol() == Clutter.KEY_Escape) { | ||||
|                                    this.cancel(); | ||||
|                                } | ||||
|                                return Clutter.EVENT_PROPAGATE; | ||||
|                            })); | ||||
|  | ||||
|         this._userWell = new St.Bin({ x_fill: true, | ||||
| @@ -93,7 +94,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|  | ||||
|         this.actor.add(this._label, | ||||
|                        { expand: true, | ||||
|                          x_fill: true, | ||||
|                          x_fill: false, | ||||
|                          y_fill: true, | ||||
|                          x_align: St.Align.START }); | ||||
|         this._entry = new St.Entry({ style_class: 'login-dialog-prompt-entry', | ||||
| @@ -111,7 +112,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|         this._message = new St.Label({ opacity: 0, | ||||
|                                        styleClass: 'login-dialog-message' }); | ||||
|         this._message.clutter_text.line_wrap = true; | ||||
|         this.actor.add(this._message, { x_fill: true, y_align: St.Align.START }); | ||||
|         this.actor.add(this._message, { x_fill: false, x_align: St.Align.START, y_align: St.Align.START }); | ||||
|  | ||||
|         this._buttonBox = new St.BoxLayout({ style_class: 'login-dialog-button-box', | ||||
|                                              vertical: false }); | ||||
| @@ -263,10 +264,8 @@ const AuthPrompt = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onReset: function() { | ||||
|         if (this.verificationStatus != AuthPromptStatus.VERIFICATION_SUCCEEDED) { | ||||
|             this.verificationStatus = AuthPromptStatus.NOT_VERIFYING; | ||||
|             this.reset(); | ||||
|         } | ||||
|         this.verificationStatus = AuthPromptStatus.NOT_VERIFYING; | ||||
|         this.reset(); | ||||
|     }, | ||||
|  | ||||
|     addActorToDefaultButtonWell: function(actor) { | ||||
| @@ -451,8 +450,7 @@ const AuthPrompt = new Lang.Class({ | ||||
|             // respond to the request with the username | ||||
|             beginRequestType = BeginRequestType.PROVIDE_USERNAME; | ||||
|         } else if (this._userVerifier.serviceIsForeground(GdmUtil.OVIRT_SERVICE_NAME) || | ||||
|                    (this.smartcardDetected && | ||||
|                     this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME))) { | ||||
|                    this._userVerifier.serviceIsForeground(GdmUtil.SMARTCARD_SERVICE_NAME)) { | ||||
|             // We don't need to know the username if the user preempted the login screen | ||||
|             // with a smartcard or with preauthenticated oVirt credentials | ||||
|             beginRequestType = BeginRequestType.DONT_PROVIDE_USERNAME; | ||||
|   | ||||
| @@ -13,9 +13,7 @@ | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||||
|  * 02111-1307, USA. | ||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| const Lang = imports.lang; | ||||
|   | ||||
| @@ -13,9 +13,7 @@ | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||||
|  * 02111-1307, USA. | ||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| const AccountsService = imports.gi.AccountsService; | ||||
| @@ -38,6 +36,7 @@ const BoxPointer = imports.ui.boxpointer; | ||||
| const CtrlAltTab = imports.ui.ctrlAltTab; | ||||
| const GdmUtil = imports.gdm.util; | ||||
| const Layout = imports.ui.layout; | ||||
| const LoginManager = imports.misc.loginManager; | ||||
| const Main = imports.ui.main; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const Realmd = imports.gdm.realmd; | ||||
| @@ -456,18 +455,6 @@ const LoginDialog = new Lang.Class({ | ||||
|         this.actor.add_child(this._logoBin); | ||||
|         this._updateLogo(); | ||||
|  | ||||
|         if (!this._userManager.is_loaded) | ||||
|             this._userManagerLoadedId = this._userManager.connect('notify::is-loaded', | ||||
|                                                                   Lang.bind(this, function() { | ||||
|                                                                       if (this._userManager.is_loaded) { | ||||
|                                                                           this._loadUserList(); | ||||
|                                                                           this._userManager.disconnect(this._userManagerLoadedId); | ||||
|                                                                           this._userManagerLoadedId = 0; | ||||
|                                                                       } | ||||
|                                                                   })); | ||||
|         else | ||||
|             this._loadUserList(); | ||||
|  | ||||
|         this._userList.connect('activate', | ||||
|                                Lang.bind(this, function(userList, item) { | ||||
|                                    this._onUserListActivated(item); | ||||
| @@ -483,7 +470,31 @@ const LoginDialog = new Lang.Class({ | ||||
|         this._sessionMenuButton.actor.show(); | ||||
|         this._authPrompt.addActorToDefaultButtonWell(this._sessionMenuButton.actor); | ||||
|  | ||||
|    }, | ||||
|         this._disableUserList = undefined; | ||||
|         this._userListLoaded = false; | ||||
|  | ||||
|         LoginManager.getLoginManager().getCurrentSessionProxy(Lang.bind(this, this._gotGreeterSessionProxy)); | ||||
|  | ||||
|         // If the user list is enabled, it should take key focus; make sure the | ||||
|         // screen shield is initialized first to prevent it from stealing the | ||||
|         // focus later | ||||
|         Main.layoutManager.connect('startup-complete', | ||||
|                                    Lang.bind(this, this._updateDisableUserList)); | ||||
|     }, | ||||
|  | ||||
|     _ensureUserListLoaded: function() { | ||||
|         if (!this._userManager.is_loaded) | ||||
|             this._userManagerLoadedId = this._userManager.connect('notify::is-loaded', | ||||
|                                                                   Lang.bind(this, function() { | ||||
|                                                                       if (this._userManager.is_loaded) { | ||||
|                                                                           this._loadUserList(); | ||||
|                                                                           this._userManager.disconnect(this._userManagerLoadedId); | ||||
|                                                                           this._userManagerLoadedId = 0; | ||||
|                                                                       } | ||||
|                                                                   })); | ||||
|         else | ||||
|             GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, this._loadUserList)); | ||||
|     }, | ||||
|  | ||||
|     _updateDisableUserList: function() { | ||||
|         let disableUserList = this._settings.get_boolean(GdmUtil.DISABLE_USER_LIST_KEY); | ||||
| @@ -526,9 +537,12 @@ const LoginDialog = new Lang.Class({ | ||||
|             return; | ||||
|  | ||||
|         this._logoBin.destroy_all_children(); | ||||
|         if (this._logoFileUri) | ||||
|         if (this._logoFileUri) { | ||||
|             let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|             this._logoBin.add_child(this._textureCache.load_uri_async(this._logoFileUri, | ||||
|                                                                       -1, _LOGO_ICON_HEIGHT)); | ||||
|                                                                       -1, _LOGO_ICON_HEIGHT, | ||||
|                                                                       scaleFactor)); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateLogo: function() { | ||||
| @@ -627,6 +641,36 @@ const LoginDialog = new Lang.Class({ | ||||
|         this._showPrompt(); | ||||
|     }, | ||||
|  | ||||
|     _loginScreenSessionActivated: function() { | ||||
|         if (this._authPrompt.verificationStatus != AuthPrompt.AuthPromptStatus.VERIFICATION_SUCCEEDED) | ||||
|             return; | ||||
|  | ||||
|         Tweener.addTween(this.actor, | ||||
|                          { opacity: 255, | ||||
|                            time: _FADE_ANIMATION_TIME, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onUpdate: function() { | ||||
|                                let children = Main.layoutManager.uiGroup.get_children(); | ||||
|  | ||||
|                                for (let i = 0; i < children.length; i++) { | ||||
|                                    if (children[i] != Main.layoutManager.screenShieldGroup) | ||||
|                                        children[i].opacity = this.actor.opacity; | ||||
|                                } | ||||
|                            }, | ||||
|                            onUpdateScope: this, | ||||
|                            onComplete: function() { | ||||
|                                this._authPrompt.reset(); | ||||
|                            }, | ||||
|                            onCompleteScope: this }); | ||||
|     }, | ||||
|  | ||||
|     _gotGreeterSessionProxy: function(proxy) { | ||||
|         proxy.connect('g-properties-changed', Lang.bind(this, function() { | ||||
|             if (proxy.Active) | ||||
|                 this._loginScreenSessionActivated(); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     _startSession: function(serviceName) { | ||||
|         Tweener.addTween(this.actor, | ||||
|                          { opacity: 0, | ||||
| @@ -644,7 +688,7 @@ const LoginDialog = new Lang.Class({ | ||||
|                            onComplete: function() { | ||||
|                                Mainloop.idle_add(Lang.bind(this, function() { | ||||
|                                    this._greeter.call_start_session_when_ready_sync(serviceName, true, null); | ||||
|                                    return false; | ||||
|                                    return GLib.SOURCE_REMOVE; | ||||
|                                })); | ||||
|                            }, | ||||
|                            onCompleteScope: this }); | ||||
| @@ -699,6 +743,7 @@ const LoginDialog = new Lang.Class({ | ||||
|                                                                      function() { | ||||
|                                                                          this._timedLoginAnimationTime -= _TIMED_LOGIN_IDLE_THRESHOLD; | ||||
|                                                                          hold.release(); | ||||
|                                                                          return GLib.SOURCE_REMOVE; | ||||
|                                                                      }); | ||||
|         return hold; | ||||
|     }, | ||||
| @@ -763,7 +808,7 @@ const LoginDialog = new Lang.Class({ | ||||
|         global.stage.connect('captured-event', | ||||
|                              Lang.bind(this, function(actor, event) { | ||||
|                                 if (this._timedLoginDelay == undefined) | ||||
|                                     return false; | ||||
|                                     return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|                                 if (event.type() == Clutter.EventType.KEY_PRESS || | ||||
|                                     event.type() == Clutter.EventType.BUTTON_PRESS) { | ||||
| @@ -776,7 +821,7 @@ const LoginDialog = new Lang.Class({ | ||||
|                                     this._resetTimedLogin(); | ||||
|                                 } | ||||
|  | ||||
|                                 return false; | ||||
|                                 return Clutter.EVENT_PROPAGATE; | ||||
|                              })); | ||||
|     }, | ||||
|  | ||||
| @@ -802,6 +847,7 @@ const LoginDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _showUserList: function() { | ||||
|         this._ensureUserListLoaded(); | ||||
|         this._authPrompt.hide(); | ||||
|         this._sessionMenuButton.close(); | ||||
|         this._setUserListExpanded(true); | ||||
| @@ -845,14 +891,17 @@ const LoginDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _loadUserList: function() { | ||||
|         if (this._userListLoaded) | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|  | ||||
|         this._userListLoaded = true; | ||||
|  | ||||
|         let users = this._userManager.list_users(); | ||||
|  | ||||
|         for (let i = 0; i < users.length; i++) { | ||||
|             this._userList.addUser(users[i]); | ||||
|         } | ||||
|  | ||||
|         this._updateDisableUserList(); | ||||
|  | ||||
|         this._userManager.connect('user-added', | ||||
|                                   Lang.bind(this, function(userManager, user) { | ||||
|                                       this._userList.addUser(user); | ||||
| @@ -862,6 +911,8 @@ const LoginDialog = new Lang.Class({ | ||||
|                                   Lang.bind(this, function(userManager, user) { | ||||
|                                       this._userList.removeUser(user); | ||||
|                                   })); | ||||
|  | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     open: function() { | ||||
| @@ -885,6 +936,10 @@ const LoginDialog = new Lang.Class({ | ||||
|         Main.ctrlAltTabManager.removeGroup(this.dialogLayout); | ||||
|     }, | ||||
|  | ||||
|     cancel: function() { | ||||
|         this._authPrompt.cancel(); | ||||
|     }, | ||||
|  | ||||
|     addCharacter: function(unichar) { | ||||
|         this._authPrompt.addCharacter(unichar); | ||||
|     }, | ||||
|   | ||||
| @@ -250,6 +250,7 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|                                                        Lang.bind(this, function() { | ||||
|                                                            this._messageQueueTimeoutId = 0; | ||||
|                                                            this._queueMessageTimeout(); | ||||
|                                                            return GLib.SOURCE_REMOVE; | ||||
|                                                        })); | ||||
|     }, | ||||
|  | ||||
| @@ -297,7 +298,7 @@ const ShellUserVerifier = new Lang.Class({ | ||||
|  | ||||
|         if (!this._settings.get_boolean(SMARTCARD_AUTHENTICATION_KEY)) | ||||
|             smartcardDetected = false; | ||||
|         else if (this.reauthenticating) | ||||
|         else if (this._reauthOnly) | ||||
|             smartcardDetected = this._smartcardManager.hasInsertedLoginToken(); | ||||
|         else | ||||
|             smartcardDetected = this._smartcardManager.hasInsertedTokens(); | ||||
|   | ||||
| @@ -8,12 +8,13 @@ | ||||
|     <file>gdm/oVirt.js</file> | ||||
|     <file>gdm/realmd.js</file> | ||||
|     <file>gdm/util.js</file> | ||||
|  | ||||
|     <file>extensionPrefs/main.js</file> | ||||
|  | ||||
|     <file>misc/config.js</file> | ||||
|     <file>misc/extensionUtils.js</file> | ||||
|     <file>misc/fileUtils.js</file> | ||||
|     <file>misc/gnomeSession.js</file> | ||||
|     <file>misc/hash.js</file> | ||||
|     <file>misc/history.js</file> | ||||
|     <file>misc/jsParse.js</file> | ||||
|     <file>misc/loginManager.js</file> | ||||
| @@ -22,7 +23,9 @@ | ||||
|     <file>misc/params.js</file> | ||||
|     <file>misc/smartcardManager.js</file> | ||||
|     <file>misc/util.js</file> | ||||
|  | ||||
|     <file>perf/core.js</file> | ||||
|  | ||||
|     <file>ui/altTab.js</file> | ||||
|     <file>ui/animation.js</file> | ||||
|     <file>ui/appDisplay.js</file> | ||||
| @@ -37,12 +40,12 @@ | ||||
|     <file>ui/dateMenu.js</file> | ||||
|     <file>ui/dnd.js</file> | ||||
|     <file>ui/endSessionDialog.js</file> | ||||
|     <file>ui/extensionSystem.js</file> | ||||
|     <file>ui/extensionDownloader.js</file> | ||||
|     <file>ui/environment.js</file> | ||||
|     <file>ui/extensionDownloader.js</file> | ||||
|     <file>ui/extensionSystem.js</file> | ||||
|     <file>ui/focusCaretTracker.js</file> | ||||
|     <file>ui/ibusCandidatePopup.js</file> | ||||
|     <file>ui/grabHelper.js</file> | ||||
|     <file>ui/ibusCandidatePopup.js</file> | ||||
|     <file>ui/iconGrid.js</file> | ||||
|     <file>ui/keyboard.js</file> | ||||
|     <file>ui/layout.js</file> | ||||
| @@ -53,11 +56,6 @@ | ||||
|     <file>ui/main.js</file> | ||||
|     <file>ui/messageTray.js</file> | ||||
|     <file>ui/modalDialog.js</file> | ||||
|     <file>ui/separator.js</file> | ||||
|     <file>ui/sessionMode.js</file> | ||||
|     <file>ui/shellEntry.js</file> | ||||
|     <file>ui/shellMountOperation.js</file> | ||||
|     <file>ui/slider.js</file> | ||||
|     <file>ui/notificationDaemon.js</file> | ||||
|     <file>ui/osdWindow.js</file> | ||||
|     <file>ui/overview.js</file> | ||||
| @@ -66,25 +64,20 @@ | ||||
|     <file>ui/panelMenu.js</file> | ||||
|     <file>ui/pointerWatcher.js</file> | ||||
|     <file>ui/popupMenu.js</file> | ||||
|     <file>ui/remoteSearch.js</file> | ||||
|     <file>ui/remoteMenu.js</file> | ||||
|     <file>ui/remoteSearch.js</file> | ||||
|     <file>ui/runDialog.js</file> | ||||
|     <file>ui/screenShield.js</file> | ||||
|     <file>ui/screencast.js</file> | ||||
|     <file>ui/screenshot.js</file> | ||||
|     <file>ui/screenShield.js</file> | ||||
|     <file>ui/scripting.js</file> | ||||
|     <file>ui/search.js</file> | ||||
|     <file>ui/separator.js</file> | ||||
|     <file>ui/sessionMode.js</file> | ||||
|     <file>ui/shellDBus.js</file> | ||||
|     <file>ui/status/accessibility.js</file> | ||||
|     <file>ui/status/brightness.js</file> | ||||
|     <file>ui/status/keyboard.js</file> | ||||
|     <file>ui/status/network.js</file> | ||||
|     <file>ui/status/power.js</file> | ||||
|     <file>ui/status/rfkill.js</file> | ||||
|     <file>ui/status/volume.js</file> | ||||
|     <file>ui/status/bluetooth.js</file> | ||||
|     <file>ui/status/screencast.js</file> | ||||
|     <file>ui/status/system.js</file> | ||||
|     <file>ui/shellEntry.js</file> | ||||
|     <file>ui/shellMountOperation.js</file> | ||||
|     <file>ui/slider.js</file> | ||||
|     <file>ui/switcherPopup.js</file> | ||||
|     <file>ui/tweener.js</file> | ||||
|     <file>ui/unlockDialog.js</file> | ||||
| @@ -93,10 +86,11 @@ | ||||
|     <file>ui/windowAttentionHandler.js</file> | ||||
|     <file>ui/windowManager.js</file> | ||||
|     <file>ui/workspace.js</file> | ||||
|     <file>ui/workspaceSwitcherPopup.js</file> | ||||
|     <file>ui/workspaceThumbnail.js</file> | ||||
|     <file>ui/workspacesView.js</file> | ||||
|     <file>ui/workspaceSwitcherPopup.js</file> | ||||
|     <file>ui/xdndHandler.js</file> | ||||
|  | ||||
|     <file>ui/components/__init__.js</file> | ||||
|     <file>ui/components/autorunManager.js</file> | ||||
|     <file>ui/components/automountManager.js</file> | ||||
| @@ -104,5 +98,17 @@ | ||||
|     <file>ui/components/polkitAgent.js</file> | ||||
|     <file>ui/components/telepathyClient.js</file> | ||||
|     <file>ui/components/keyring.js</file> | ||||
|  | ||||
|     <file>ui/status/accessibility.js</file> | ||||
|     <file>ui/status/brightness.js</file> | ||||
|     <file>ui/status/location.js</file> | ||||
|     <file>ui/status/keyboard.js</file> | ||||
|     <file>ui/status/network.js</file> | ||||
|     <file>ui/status/power.js</file> | ||||
|     <file>ui/status/rfkill.js</file> | ||||
|     <file>ui/status/volume.js</file> | ||||
|     <file>ui/status/bluetooth.js</file> | ||||
|     <file>ui/status/screencast.js</file> | ||||
|     <file>ui/status/system.js</file> | ||||
|   </gresource> | ||||
| </gresources> | ||||
|   | ||||
| @@ -6,6 +6,8 @@ const PACKAGE_NAME = '@PACKAGE_NAME@'; | ||||
| const PACKAGE_VERSION = '@PACKAGE_VERSION@'; | ||||
| /* 1 if gnome-bluetooth is available, 0 otherwise */ | ||||
| const HAVE_BLUETOOTH = @HAVE_BLUETOOTH@; | ||||
| /* 1 if networkmanager is available, 0 otherwise */ | ||||
| const HAVE_NETWORKMANAGER = @HAVE_NETWORKMANAGER@; | ||||
| /* gettext package */ | ||||
| const GETTEXT_PACKAGE = '@GETTEXT_PACKAGE@'; | ||||
| /* locale dir */ | ||||
|   | ||||
| @@ -5,26 +5,6 @@ const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Params = imports.misc.params; | ||||
|  | ||||
| function listDirAsync(file, callback) { | ||||
|     let allFiles = []; | ||||
|     file.enumerate_children_async('standard::name,standard::type', | ||||
|                                   Gio.FileQueryInfoFlags.NONE, | ||||
|                                   GLib.PRIORITY_LOW, null, function (obj, res) { | ||||
|         let enumerator = obj.enumerate_children_finish(res); | ||||
|         function onNextFileComplete(obj, res) { | ||||
|             let files = obj.next_files_finish(res); | ||||
|             if (files.length) { | ||||
|                 allFiles = allFiles.concat(files); | ||||
|                 enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, onNextFileComplete); | ||||
|             } else { | ||||
|                 enumerator.close(null); | ||||
|                 callback(allFiles); | ||||
|             } | ||||
|         } | ||||
|         enumerator.next_files_async(100, GLib.PRIORITY_LOW, null, onNextFileComplete); | ||||
|     }); | ||||
| } | ||||
|  | ||||
| function collectFromDatadirs(subdir, includeUserDir, processFile) { | ||||
|     let dataDirs = GLib.get_system_data_dirs(); | ||||
|     if (includeUserDir) | ||||
|   | ||||
							
								
								
									
										144
									
								
								js/misc/hash.js
									
									
									
									
									
								
							
							
						
						
									
										144
									
								
								js/misc/hash.js
									
									
									
									
									
								
							| @@ -1,144 +0,0 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| const System = imports.system; | ||||
|  | ||||
| const Params = imports.misc.params; | ||||
|  | ||||
| // This is an implementation of EcmaScript SameValue algorithm, | ||||
| // which returns true if two values are not observably distinguishable. | ||||
| // It was taken from http://wiki.ecmascript.org/doku.php?id=harmony:egal | ||||
| // | ||||
| // In the future, we may want to use the 'is' operator instead. | ||||
| function _sameValue(x, y) { | ||||
|     if (x === y) { | ||||
|         // 0 === -0, but they are not identical | ||||
|         return x !== 0 || 1 / x === 1 / y; | ||||
|     } | ||||
|  | ||||
|     // NaN !== NaN, but they are identical. | ||||
|     // NaNs are the only non-reflexive value, i.e., if x !== x, | ||||
|     // then x is a NaN. | ||||
|     // isNaN is broken: it converts its argument to number, so | ||||
|     // isNaN("foo") => true | ||||
|     return x !== x && y !== y; | ||||
| } | ||||
|  | ||||
| const _hashers = { | ||||
|     object: function(o) { return o ? System.addressOf(o) : 'null'; }, | ||||
|     function: function(f) { return System.addressOf(f); }, | ||||
|     string: function(s) { return s; }, | ||||
|     number: function(n) { return String(n); }, | ||||
|     undefined: function() { return 'undefined'; }, | ||||
| }; | ||||
|  | ||||
| /* Map is meant to be similar in usage to ES6 Map, which is | ||||
|    described at http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets, | ||||
|    without requiring more than ES5 + Gjs extensions. | ||||
|  | ||||
|    Known differences from other implementations: | ||||
|    Polyfills around the web usually implement HashMaps for | ||||
|    primitive values and reversed WeakMaps for object keys, | ||||
|    but we want real maps with real O(1) semantics in all cases, | ||||
|    and the easiest way is to have different hashers for different | ||||
|    types. | ||||
|  | ||||
|    Known differences from the ES6 specification: | ||||
|    - Map is a Lang.Class, not a ES6 class, so inheritance, | ||||
|      prototype, sealing, etc. work differently. | ||||
|    - items(), keys() and values() don't return iterators, | ||||
|      they return actual arrays, so they incur a full copy everytime | ||||
|      they're called, and they don't see changes if you mutate | ||||
|      the table while iterating | ||||
|      (admittedly, the ES6 spec is a bit unclear on this, and | ||||
|      the reference code would just blow up) | ||||
| */ | ||||
| const Map = new Lang.Class({ | ||||
|     Name: 'Map', | ||||
|  | ||||
|     _init: function(iterable) { | ||||
|         this._pool = { }; | ||||
|         this._size = 0; | ||||
|  | ||||
|         if (iterable) { | ||||
|             for (let i = 0; i < iterable.length; i++) { | ||||
|                 let [key, value] = iterable[i]; | ||||
|                 this.set(key, value); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _hashKey: function(key) { | ||||
|         let type = typeof(key); | ||||
|         return type + ':' + _hashers[type](key); | ||||
|     }, | ||||
|  | ||||
|     _internalGet: function(key) { | ||||
|         let hash = this._hashKey(key); | ||||
|         let node = this._pool[hash]; | ||||
|  | ||||
|         if (node && _sameValue(node.key, key)) | ||||
|             return [true, node.value]; | ||||
|         else | ||||
|             return [false, null]; | ||||
|     }, | ||||
|  | ||||
|     get: function(key) { | ||||
|         return this._internalGet(key)[1]; | ||||
|     }, | ||||
|  | ||||
|     has: function(key) { | ||||
|         return this._internalGet(key)[0]; | ||||
|     }, | ||||
|  | ||||
|     set: function(key, value) { | ||||
|         let hash = this._hashKey(key); | ||||
|         let node = this._pool[hash]; | ||||
|  | ||||
|         if (node) { | ||||
|             node.key = key; | ||||
|             node.value = value; | ||||
|         } else { | ||||
|             this._pool[hash] = { key: key, value: value }; | ||||
|             this._size++; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     delete: function(key) { | ||||
|         let hash = this._hashKey(key); | ||||
|         let node = this._pool[hash]; | ||||
|  | ||||
|         if (node && _sameValue(node.key, key)) { | ||||
|             delete this._pool[hash]; | ||||
|             this._size--; | ||||
|             return [node.key, node.value]; | ||||
|         } else { | ||||
|             return [null, null]; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     keys: function() { | ||||
|         let pool = this._pool; | ||||
|         return Object.getOwnPropertyNames(pool).map(function(hash) { | ||||
|             return pool[hash].key; | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     values: function() { | ||||
|         let pool = this._pool; | ||||
|         return Object.getOwnPropertyNames(pool).map(function(hash) { | ||||
|             return pool[hash].value; | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     items: function() { | ||||
|         let pool = this._pool; | ||||
|         return Object.getOwnPropertyNames(pool).map(function(hash) { | ||||
|             return [pool[hash].key, pool[hash].value]; | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     size: function() { | ||||
|         return this._size; | ||||
|     }, | ||||
| }); | ||||
| @@ -89,7 +89,7 @@ const HistoryManager = new Lang.Class({ | ||||
|         } else if (symbol == Clutter.KEY_Down) { | ||||
|             return this._setNextItem(entry.get_text()); | ||||
|         } | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _indexChanged: function() { | ||||
|   | ||||
| @@ -39,6 +39,7 @@ const SystemdLoginSessionIface = '<node> \ | ||||
| <interface name="org.freedesktop.login1.Session"> \ | ||||
| <signal name="Lock" /> \ | ||||
| <signal name="Unlock" /> \ | ||||
| <property name="Active" type="b" access="read" /> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
|   | ||||
| @@ -89,7 +89,7 @@ function spawnApp(argv) { | ||||
|         let app = Gio.AppInfo.create_from_commandline(argv.join(' '), null, | ||||
|                                                       Gio.AppInfoCreateFlags.SUPPORTS_STARTUP_NOTIFICATION); | ||||
|  | ||||
|         let context = global.create_app_launch_context(); | ||||
|         let context = global.create_app_launch_context(0, -1); | ||||
|         app.launch([], context); | ||||
|     } catch(err) { | ||||
|         _handleSpawnError(argv[0], err); | ||||
| @@ -153,7 +153,7 @@ function trySpawnCommandLine(command_line) { | ||||
| } | ||||
|  | ||||
| function _handleSpawnError(command, err) { | ||||
|     let title = _("Execution of '%s' failed:").format(command); | ||||
|     let title = _("Execution of “%s” failed:").format(command); | ||||
|     Main.notifyError(title, err.message); | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Meta = imports.gi.Meta; | ||||
| @@ -23,7 +24,7 @@ const WINDOW_PREVIEW_SIZE = 128; | ||||
| const APP_ICON_SIZE = 96; | ||||
| const APP_ICON_SIZE_SMALL = 48; | ||||
|  | ||||
| const iconSizes = [96, 64, 48, 32, 22]; | ||||
| const baseIconSizes = [96, 64, 48, 32, 22]; | ||||
|  | ||||
| const AppIconMode = { | ||||
|     THUMBNAIL_ONLY: 1, | ||||
| @@ -166,6 +167,8 @@ const AppSwitcherPopup = new Lang.Class({ | ||||
|                 this._select(this._selectedIndex, this._nextWindow()); | ||||
|             else if (keysym == Clutter.Up) | ||||
|                 this._select(this._selectedIndex, null, true); | ||||
|             else | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|         } else { | ||||
|             if (keysym == Clutter.Left) | ||||
|                 this._select(this._previous()); | ||||
| @@ -173,7 +176,11 @@ const AppSwitcherPopup = new Lang.Class({ | ||||
|                 this._select(this._next()); | ||||
|             else if (keysym == Clutter.Down) | ||||
|                 this._select(this._selectedIndex, 0); | ||||
|             else | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|         } | ||||
|  | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _scrollHandler: function(direction) { | ||||
| @@ -312,7 +319,7 @@ const AppSwitcherPopup = new Lang.Class({ | ||||
|             this._createThumbnails(); | ||||
|         this._thumbnailTimeoutId = 0; | ||||
|         this._thumbnailsFocused = false; | ||||
|         return false; | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     _destroyThumbnails : function() { | ||||
| @@ -402,7 +409,11 @@ const WindowSwitcherPopup = new Lang.Class({ | ||||
|                 this._select(this._previous()); | ||||
|             else if (keysym == Clutter.Right) | ||||
|                 this._select(this._next()); | ||||
|             else | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|         } | ||||
|  | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _finish: function() { | ||||
| @@ -429,7 +440,6 @@ const AppIcon = new Lang.Class({ | ||||
|  | ||||
|     set_size: function(size) { | ||||
|         this.icon = this.app.create_icon_texture(size); | ||||
|         this._iconBin.set_size(size, size); | ||||
|         this._iconBin.child = this.icon; | ||||
|     } | ||||
| }); | ||||
| @@ -459,9 +469,10 @@ const AppSwitcher = new Lang.Class({ | ||||
|             appIcon.cachedWindows = allWindows.filter(function(w) { | ||||
|                 return windowTracker.get_window_app (w) == appIcon.app; | ||||
|             }); | ||||
|             if (workspace == null || appIcon.cachedWindows.length > 0) { | ||||
|             if (appIcon.cachedWindows.length > 0) | ||||
|                 this._addIcon(appIcon); | ||||
|             } | ||||
|             else if (workspace == null) | ||||
|                 throw new Error('%s appears to be running, but doesn\'t have any windows'.format(appIcon.app.get_name())); | ||||
|         } | ||||
|  | ||||
|         this._curApp = -1; | ||||
| @@ -477,12 +488,13 @@ const AppSwitcher = new Lang.Class({ | ||||
|             Mainloop.source_remove(this._mouseTimeOutId); | ||||
|     }, | ||||
|  | ||||
|     _getPreferredHeight: function (actor, forWidth, alloc) { | ||||
|     _setIconSize: function() { | ||||
|         let j = 0; | ||||
|         while(this._items.length > 1 && this._items[j].style_class != 'item-box') { | ||||
|                 j++; | ||||
|         } | ||||
|         let themeNode = this._items[j].get_theme_node(); | ||||
|  | ||||
|         let iconPadding = themeNode.get_horizontal_padding(); | ||||
|         let iconBorder = themeNode.get_border_width(St.Side.LEFT) + themeNode.get_border_width(St.Side.RIGHT); | ||||
|         let [iconMinHeight, iconNaturalHeight] = this.icons[j].label.get_preferred_height(-1); | ||||
| @@ -493,19 +505,22 @@ const AppSwitcher = new Lang.Class({ | ||||
|         let primary = Main.layoutManager.primaryMonitor; | ||||
|         let parentPadding = this.actor.get_parent().get_theme_node().get_horizontal_padding(); | ||||
|         let availWidth = primary.width - parentPadding - this.actor.get_theme_node().get_horizontal_padding(); | ||||
|         let height = 0; | ||||
|  | ||||
|         for(let i =  0; i < iconSizes.length; i++) { | ||||
|                 this._iconSize = iconSizes[i]; | ||||
|                 height = iconSizes[i] + iconSpacing; | ||||
|                 let w = height * this._items.length + totalSpacing; | ||||
|                 if (w <= availWidth) | ||||
|                         break; | ||||
|         } | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|         let iconSizes = baseIconSizes.map(function(s) { | ||||
|             return s * scaleFactor; | ||||
|         }); | ||||
|  | ||||
|         if (this._items.length == 1) { | ||||
|             this._iconSize = iconSizes[0]; | ||||
|             height = iconSizes[0] + iconSpacing; | ||||
|             this._iconSize = baseIconSizes[0]; | ||||
|         } else { | ||||
|             for(let i =  0; i < baseIconSizes.length; i++) { | ||||
|                 this._iconSize = baseIconSizes[i]; | ||||
|                 let height = iconSizes[i] + iconSpacing; | ||||
|                 let w = height * this._items.length + totalSpacing; | ||||
|                 if (w <= availWidth) | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         for(let i = 0; i < this.icons.length; i++) { | ||||
| @@ -513,9 +528,11 @@ const AppSwitcher = new Lang.Class({ | ||||
|                 break; | ||||
|             this.icons[i].set_size(this._iconSize); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|         alloc.min_size = height; | ||||
|         alloc.natural_size = height; | ||||
|     _getPreferredHeight: function (actor, forWidth, alloc) { | ||||
|         this._setIconSize(); | ||||
|         this.parent(actor, forWidth, alloc); | ||||
|     }, | ||||
|  | ||||
|     _allocate: function (actor, box, flags) { | ||||
| @@ -547,7 +564,7 @@ const AppSwitcher = new Lang.Class({ | ||||
|                                                         Lang.bind(this, function () { | ||||
|                                                                             this._enterItem(index); | ||||
|                                                                             this._mouseTimeOutId = 0; | ||||
|                                                                             return false; | ||||
|                                                                             return GLib.SOURCE_REMOVE; | ||||
|                                                         })); | ||||
|         } else | ||||
|            this._itemEntered(index); | ||||
| @@ -648,17 +665,19 @@ const ThumbnailList = new Lang.Class({ | ||||
|         totalPadding += this.actor.get_theme_node().get_horizontal_padding() + this.actor.get_theme_node().get_vertical_padding(); | ||||
|         let [labelMinHeight, labelNaturalHeight] = this._labels[0].get_preferred_height(-1); | ||||
|         let spacing = this._items[0].child.get_theme_node().get_length('spacing'); | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|         let thumbnailSize = THUMBNAIL_DEFAULT_SIZE * scaleFactor; | ||||
|  | ||||
|         availHeight = Math.min(availHeight - labelNaturalHeight - totalPadding - spacing, THUMBNAIL_DEFAULT_SIZE); | ||||
|         availHeight = Math.min(availHeight - labelNaturalHeight - totalPadding - spacing, thumbnailSize); | ||||
|         let binHeight = availHeight + this._items[0].get_theme_node().get_vertical_padding() + this.actor.get_theme_node().get_vertical_padding() - spacing; | ||||
|         binHeight = Math.min(THUMBNAIL_DEFAULT_SIZE, binHeight); | ||||
|         binHeight = Math.min(thumbnailSize, binHeight); | ||||
|  | ||||
|         for (let i = 0; i < this._thumbnailBins.length; i++) { | ||||
|             let mutterWindow = this._windows[i].get_compositor_private(); | ||||
|             if (!mutterWindow) | ||||
|                 continue; | ||||
|  | ||||
|             let clone = _createWindowClone(mutterWindow, THUMBNAIL_DEFAULT_SIZE); | ||||
|             let clone = _createWindowClone(mutterWindow, thumbnailSize); | ||||
|             this._thumbnailBins[i].set_height(binHeight); | ||||
|             this._thumbnailBins[i].add_actor(clone); | ||||
|             this._clones.push(clone); | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const St = imports.gi.St; | ||||
| @@ -20,7 +21,9 @@ const Animation = new Lang.Class({ | ||||
|         this._isPlaying = false; | ||||
|         this._timeoutId = 0; | ||||
|         this._frame = 0; | ||||
|         this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height, | ||||
|  | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|         this._animations = St.TextureCache.get_default().load_sliced_image (filename, width, height, scaleFactor, | ||||
|                                                                             Lang.bind(this, this._animationsLoaded)); | ||||
|         this.actor.set_child(this._animations); | ||||
|     }, | ||||
| @@ -59,7 +62,7 @@ const Animation = new Lang.Class({ | ||||
|  | ||||
|     _update: function() { | ||||
|         this._showFrame(this._frame + 1); | ||||
|         return true; | ||||
|         return GLib.SOURCE_CONTINUE; | ||||
|     }, | ||||
|  | ||||
|     _animationsLoaded: function() { | ||||
|   | ||||
| @@ -5,7 +5,6 @@ const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const GObject = imports.gi.GObject; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const GMenu = imports.gi.GMenu; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Lang = imports.lang; | ||||
| const Signals = imports.signals; | ||||
| @@ -47,24 +46,40 @@ const INDICATORS_ANIMATION_MAX_TIME = 0.75; | ||||
| const PAGE_SWITCH_TRESHOLD = 0.2; | ||||
| const PAGE_SWITCH_TIME = 0.3; | ||||
|  | ||||
| // Recursively load a GMenuTreeDirectory; we could put this in ShellAppSystem too | ||||
| function _loadCategory(dir, view) { | ||||
|     let iter = dir.iter(); | ||||
|     let appSystem = Shell.AppSystem.get_default(); | ||||
|     let nextType; | ||||
|     while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) { | ||||
|         if (nextType == GMenu.TreeItemType.ENTRY) { | ||||
|             let entry = iter.get_entry(); | ||||
|             let appInfo = entry.get_app_info(); | ||||
|             let app = appSystem.lookup_app(entry.get_desktop_file_id()); | ||||
|             if (appInfo.should_show()) | ||||
|                 view.addApp(app); | ||||
|         } else if (nextType == GMenu.TreeItemType.DIRECTORY) { | ||||
|             let itemDir = iter.get_directory(); | ||||
|             _loadCategory(itemDir, view); | ||||
| const VIEWS_SWITCH_TIME = 0.4; | ||||
| const VIEWS_SWITCH_ANIMATION_DELAY = 0.1; | ||||
|  | ||||
| function _getCategories(info) { | ||||
|     let categoriesStr = info.get_categories(); | ||||
|     if (!categoriesStr) | ||||
|         return []; | ||||
|     return categoriesStr.split(';'); | ||||
| } | ||||
|  | ||||
| function _listsIntersect(a, b) { | ||||
|     for (let itemA of a) | ||||
|         if (b.indexOf(itemA) >= 0) | ||||
|             return true; | ||||
|     return false; | ||||
| } | ||||
|  | ||||
| function _getFolderName(folder) { | ||||
|     let name = folder.get_string('name'); | ||||
|  | ||||
|     if (folder.get_boolean('translate')) { | ||||
|         let keyfile = new GLib.KeyFile(); | ||||
|         let path = 'desktop-directories/' + name; | ||||
|  | ||||
|         try { | ||||
|             keyfile.load_from_data_dirs(path, GLib.KeyFileFlags.NONE); | ||||
|             name = keyfile.get_locale_string('Desktop Entry', 'Name', null); | ||||
|         } catch(e) { | ||||
|             return name; | ||||
|         } | ||||
|     } | ||||
| }; | ||||
|  | ||||
|     return name; | ||||
| } | ||||
|  | ||||
| const BaseAppView = new Lang.Class({ | ||||
|     Name: 'BaseAppView', | ||||
| @@ -84,6 +99,9 @@ const BaseAppView = new Lang.Class({ | ||||
|         else | ||||
|             this._grid = new IconGrid.IconGrid(gridParams); | ||||
|  | ||||
|         this._grid.connect('key-focus-in', Lang.bind(this, function(grid, actor) { | ||||
|             this._keyFocusIn(actor); | ||||
|         })); | ||||
|         // Standard hack for ClutterBinLayout | ||||
|         this._grid.actor.x_expand = true; | ||||
|  | ||||
| @@ -91,46 +109,43 @@ const BaseAppView = new Lang.Class({ | ||||
|         this._allItems = []; | ||||
|     }, | ||||
|  | ||||
|     _keyFocusIn: function(actor) { | ||||
|         // Nothing by default | ||||
|     }, | ||||
|  | ||||
|     removeAll: function() { | ||||
|         this._grid.destroyAll(); | ||||
|         this._items = {}; | ||||
|         this._allItems = []; | ||||
|     }, | ||||
|  | ||||
|     _getItemId: function(item) { | ||||
|         throw new Error('Not implemented'); | ||||
|     _redisplay: function() { | ||||
|         this.removeAll(); | ||||
|         this._loadApps(); | ||||
|     }, | ||||
|  | ||||
|     _createItemIcon: function(item) { | ||||
|         throw new Error('Not implemented'); | ||||
|     getAllItems: function() { | ||||
|         return this._allItems; | ||||
|     }, | ||||
|  | ||||
|     addItem: function(icon) { | ||||
|         let id = icon.id; | ||||
|         if (this._items[id] !== undefined) | ||||
|             return; | ||||
|  | ||||
|         this._allItems.push(icon); | ||||
|         this._items[id] = icon; | ||||
|     }, | ||||
|  | ||||
|     _compareItems: function(a, b) { | ||||
|         throw new Error('Not implemented'); | ||||
|     }, | ||||
|  | ||||
|     _addItem: function(item) { | ||||
|         let id = this._getItemId(item); | ||||
|         if (this._items[id] !== undefined) | ||||
|             return null; | ||||
|  | ||||
|         let itemIcon = this._createItemIcon(item); | ||||
|         this._allItems.push(item); | ||||
|         this._items[id] = itemIcon; | ||||
|  | ||||
|         return itemIcon; | ||||
|         return a.name.localeCompare(b.name); | ||||
|     }, | ||||
|  | ||||
|     loadGrid: function() { | ||||
|         this._allItems.sort(Lang.bind(this, this._compareItems)); | ||||
|  | ||||
|         for (let i = 0; i < this._allItems.length; i++) { | ||||
|             let id = this._getItemId(this._allItems[i]); | ||||
|             if (!id) | ||||
|                 continue; | ||||
|             this._grid.addItem(this._items[id]); | ||||
|         } | ||||
|  | ||||
|         this._allItems.sort(this._compareItems); | ||||
|         this._allItems.forEach(Lang.bind(this, function(item) { | ||||
|             this._grid.addItem(item); | ||||
|         })); | ||||
|         this.emit('view-loaded'); | ||||
|     }, | ||||
|  | ||||
| @@ -243,7 +258,8 @@ const PageIndicators = new Lang.Class({ | ||||
|             Tweener.addTween(children[i], | ||||
|                              { translation_x: 0, | ||||
|                                time: INDICATORS_BASE_TIME + delay * i, | ||||
|                                transition: 'easeInOutQuad' | ||||
|                                transition: 'easeInOutQuad', | ||||
|                                delay: VIEWS_SWITCH_ANIMATION_DELAY | ||||
|                              }); | ||||
|         } | ||||
|     } | ||||
| @@ -281,7 +297,7 @@ const AllView = new Lang.Class({ | ||||
|         this._pageIndicators.actor.connect('scroll-event', Lang.bind(this, this._onScroll)); | ||||
|         this.actor.add_actor(this._pageIndicators.actor); | ||||
|  | ||||
|         this._folderIcons = []; | ||||
|         this.folderIcons = []; | ||||
|  | ||||
|         this._stack = new St.Widget({ layout_manager: new Clutter.BinLayout() }); | ||||
|         let box = new St.BoxLayout({ vertical: true }); | ||||
| @@ -346,6 +362,76 @@ const AllView = new Lang.Class({ | ||||
|                     this._keyPressEventId = 0; | ||||
|                 } | ||||
|             })); | ||||
|  | ||||
|         this._redisplayWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplay)); | ||||
|  | ||||
|         Shell.AppSystem.get_default().connect('installed-changed', Lang.bind(this, function() { | ||||
|             Main.queueDeferredWork(this._redisplayWorkId); | ||||
|         })); | ||||
|         this._folderSettings = new Gio.Settings({ schema: 'org.gnome.desktop.app-folders' }); | ||||
|         this._folderSettings.connect('changed::folder-children', Lang.bind(this, function() { | ||||
|             Main.queueDeferredWork(this._redisplayWorkId); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     removeAll: function() { | ||||
|         this.folderIcons = []; | ||||
|         this.parent(); | ||||
|     }, | ||||
|  | ||||
|     _itemNameChanged: function(item) { | ||||
|         // If an item's name changed, we can pluck it out of where it's | ||||
|         // supposed to be and reinsert it where it's sorted. | ||||
|         let oldIdx = this._allItems.indexOf(item); | ||||
|         this._allItems.splice(oldIdx, 1); | ||||
|         let newIdx = Util.insertSorted(this._allItems, item, this._compareItems); | ||||
|  | ||||
|         this._grid.removeItem(item); | ||||
|         this._grid.addItem(item, newIdx); | ||||
|     }, | ||||
|  | ||||
|     _refilterApps: function() { | ||||
|         this._allItems.forEach(function(icon) { | ||||
|             if (icon instanceof AppIcon) | ||||
|                 icon.actor.visible = true; | ||||
|         }); | ||||
|  | ||||
|         this.folderIcons.forEach(Lang.bind(this, function(folder) { | ||||
|             let folderApps = folder.getAppIds(); | ||||
|             folderApps.forEach(Lang.bind(this, function(appId) { | ||||
|                 let appIcon = this._items[appId]; | ||||
|                 appIcon.actor.visible = false; | ||||
|             })); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     _loadApps: function() { | ||||
|         let apps = Gio.AppInfo.get_all().filter(function(appInfo) { | ||||
|             return appInfo.should_show(); | ||||
|         }).map(function(app) { | ||||
|             return app.get_id(); | ||||
|         }); | ||||
|  | ||||
|         let appSys = Shell.AppSystem.get_default(); | ||||
|  | ||||
|         let folders = this._folderSettings.get_strv('folder-children'); | ||||
|         folders.forEach(Lang.bind(this, function(id) { | ||||
|             let path = this._folderSettings.path + 'folders/' + id + '/'; | ||||
|             let icon = new FolderIcon(id, path, this); | ||||
|             icon.connect('name-changed', Lang.bind(this, this._itemNameChanged)); | ||||
|             icon.connect('apps-changed', Lang.bind(this, this._refilterApps)); | ||||
|             this.addItem(icon); | ||||
|             this.folderIcons.push(icon); | ||||
|         })); | ||||
|  | ||||
|         apps.forEach(Lang.bind(this, function(appId) { | ||||
|             let app = appSys.lookup_app(appId); | ||||
|             let icon = new AppIcon(app); | ||||
|             this.addItem(icon); | ||||
|         })); | ||||
|  | ||||
|         this.loadGrid(); | ||||
|         this._refilterApps(); | ||||
|     }, | ||||
|  | ||||
|     getCurrentPageY: function() { | ||||
| @@ -415,7 +501,7 @@ const AllView = new Lang.Class({ | ||||
|  | ||||
|     _onScroll: function(actor, event) { | ||||
|         if (this._displayingPopup) | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|  | ||||
|         let direction = event.get_scroll_direction(); | ||||
|         if (direction == Clutter.ScrollDirection.UP) | ||||
| @@ -423,7 +509,7 @@ const AllView = new Lang.Class({ | ||||
|         else if (direction == Clutter.ScrollDirection.DOWN) | ||||
|             this.goToPage(this._currentPage + 1); | ||||
|  | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _onPan: function(action) { | ||||
| @@ -454,63 +540,17 @@ const AllView = new Lang.Class({ | ||||
|  | ||||
|     _onKeyPressEvent: function(actor, event) { | ||||
|         if (this._displayingPopup) | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|  | ||||
|         if (event.get_key_symbol() == Clutter.Page_Up) { | ||||
|             this.goToPage(this._currentPage - 1); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } else if (event.get_key_symbol() == Clutter.Page_Down) { | ||||
|             this.goToPage(this._currentPage + 1); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _getItemId: function(item) { | ||||
|         if (item instanceof Shell.App) | ||||
|             return item.get_id(); | ||||
|         else if (item instanceof GMenu.TreeDirectory) | ||||
|             return item.get_menu_id(); | ||||
|         else | ||||
|             return null; | ||||
|     }, | ||||
|  | ||||
|     _createItemIcon: function(item) { | ||||
|         if (item instanceof Shell.App) | ||||
|             return new AppIcon(item); | ||||
|         else if (item instanceof GMenu.TreeDirectory) | ||||
|             return new FolderIcon(item, this); | ||||
|         else | ||||
|             return null; | ||||
|     }, | ||||
|  | ||||
|     _compareItems: function(itemA, itemB) { | ||||
|         // bit of a hack: rely on both ShellApp and GMenuTreeDirectory | ||||
|         // having a get_name() method | ||||
|         let nameA = GLib.utf8_collate_key(itemA.get_name(), -1); | ||||
|         let nameB = GLib.utf8_collate_key(itemB.get_name(), -1); | ||||
|         return (nameA > nameB) ? 1 : (nameA < nameB ? -1 : 0); | ||||
|     }, | ||||
|  | ||||
|     removeAll: function() { | ||||
|         this._folderIcons = []; | ||||
|         this.parent(); | ||||
|     }, | ||||
|  | ||||
|     addApp: function(app) { | ||||
|         let appIcon = this._addItem(app); | ||||
|         if (appIcon) | ||||
|             appIcon.actor.connect('key-focus-in', | ||||
|                                   Lang.bind(this, this._ensureIconVisible)); | ||||
|     }, | ||||
|  | ||||
|     addFolder: function(dir) { | ||||
|         let folderIcon = this._addItem(dir); | ||||
|         this._folderIcons.push(folderIcon); | ||||
|         if (folderIcon) | ||||
|             folderIcon.actor.connect('key-focus-in', | ||||
|                                      Lang.bind(this, this._ensureIconVisible)); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     addFolderPopup: function(popup) { | ||||
| @@ -525,7 +565,7 @@ const AllView = new Lang.Class({ | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     _ensureIconVisible: function(icon) { | ||||
|     _keyFocusIn: function(icon) { | ||||
|         let itemPage = this._grid.getItemPage(icon); | ||||
|         this.goToPage(itemPage); | ||||
|     }, | ||||
| @@ -577,8 +617,8 @@ const AllView = new Lang.Class({ | ||||
|         this._availWidth = availWidth; | ||||
|         this._availHeight = availHeight; | ||||
|         // Update folder views | ||||
|         for (let i = 0; i < this._folderIcons.length; i++) | ||||
|             this._folderIcons[i].adaptToSize(availWidth, availHeight); | ||||
|         for (let i = 0; i < this.folderIcons.length; i++) | ||||
|             this.folderIcons[i].adaptToSize(availWidth, availHeight); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(AllView.prototype); | ||||
| @@ -607,17 +647,18 @@ const FrequentView = new Lang.Class({ | ||||
|         this._noFrequentAppsLabel.hide(); | ||||
|  | ||||
|         this._usage = Shell.AppUsage.get_default(); | ||||
|  | ||||
|         this.actor.connect('notify::mapped', Lang.bind(this, function() { | ||||
|             if (this.actor.mapped) | ||||
|                 this._redisplay(); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     hasUsefulData: function() { | ||||
|         return this._usage.get_most_used("").length >= MIN_FREQUENT_APPS_COUNT; | ||||
|     }, | ||||
|  | ||||
|     removeAll: function() { | ||||
|         this._grid.destroyAll(); | ||||
|     }, | ||||
|  | ||||
|     loadApps: function() { | ||||
|     _loadApps: function() { | ||||
|         let mostUsed = this._usage.get_most_used (""); | ||||
|         let hasUsefulData = this.hasUsefulData(); | ||||
|         this._noFrequentAppsLabel.visible = !hasUsefulData; | ||||
| @@ -695,15 +736,6 @@ const AppDisplay = new Lang.Class({ | ||||
|     Name: 'AppDisplay', | ||||
|  | ||||
|     _init: function() { | ||||
|         Shell.AppSystem.get_default().connect('installed-changed', Lang.bind(this, function() { | ||||
|             Main.queueDeferredWork(this._allAppsWorkId); | ||||
|         })); | ||||
|         Main.overview.connect('showing', Lang.bind(this, function() { | ||||
|             Main.queueDeferredWork(this._frequentAppsWorkId); | ||||
|         })); | ||||
|         global.settings.connect('changed::app-folder-categories', Lang.bind(this, function() { | ||||
|             Main.queueDeferredWork(this._allAppsWorkId); | ||||
|         })); | ||||
|         this._privacySettings = new Gio.Settings({ schema: 'org.gnome.desktop.privacy' }); | ||||
|         this._privacySettings.connect('changed::remember-app-usage', | ||||
|                                       Lang.bind(this, this._updateFrequentVisibility)); | ||||
| @@ -757,22 +789,15 @@ const AppDisplay = new Lang.Class({ | ||||
|             initialView = Views.ALL; | ||||
|         this._showView(initialView); | ||||
|         this._updateFrequentVisibility(); | ||||
|  | ||||
|         // We need a dummy actor to catch the keyboard focus if the | ||||
|         // user Ctrl-Alt-Tabs here before the deferred work creates | ||||
|         // our real contents | ||||
|         this._focusDummy = new St.Bin({ can_focus: true }); | ||||
|         this._viewStack.add_actor(this._focusDummy); | ||||
|  | ||||
|         this._allAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayAllApps)); | ||||
|         this._frequentAppsWorkId = Main.initializeDeferredWork(this.actor, Lang.bind(this, this._redisplayFrequentApps)); | ||||
|     }, | ||||
|  | ||||
|     _showView: function(activeIndex) { | ||||
|         for (let i = 0; i < this._views.length; i++) { | ||||
|             let actor = this._views[i].view.actor; | ||||
|             let params = { time: OverviewControls.SIDE_CONTROLS_ANIMATION_TIME, | ||||
|                            opacity: (i == activeIndex) ? 255 : 0 }; | ||||
|  | ||||
|             let params = { time: VIEWS_SWITCH_TIME, | ||||
|                            opacity: (i == activeIndex) ? 255 : 0, | ||||
|                            delay: (i == activeIndex) ? VIEWS_SWITCH_ANIMATION_DELAY : 0 }; | ||||
|             if (i == activeIndex) | ||||
|                 actor.visible = true; | ||||
|             else | ||||
| @@ -799,51 +824,6 @@ const AppDisplay = new Lang.Class({ | ||||
|             this._showView(Views.ALL); | ||||
|     }, | ||||
|  | ||||
|     _redisplay: function() { | ||||
|         this._redisplayFrequentApps(); | ||||
|         this._redisplayAllApps(); | ||||
|     }, | ||||
|  | ||||
|     _redisplayFrequentApps: function() { | ||||
|         let view = this._views[Views.FREQUENT].view; | ||||
|  | ||||
|         view.removeAll(); | ||||
|         view.loadApps(); | ||||
|     }, | ||||
|  | ||||
|     _redisplayAllApps: function() { | ||||
|         let view = this._views[Views.ALL].view; | ||||
|  | ||||
|         view.removeAll(); | ||||
|  | ||||
|         let tree = new GMenu.Tree({ menu_basename: "applications.menu" }); | ||||
|         tree.load_sync(); | ||||
|         let root = tree.get_root_directory(); | ||||
|  | ||||
|         let iter = root.iter(); | ||||
|         let nextType; | ||||
|         let folderCategories = global.settings.get_strv('app-folder-categories'); | ||||
|         while ((nextType = iter.next()) != GMenu.TreeItemType.INVALID) { | ||||
|             if (nextType == GMenu.TreeItemType.DIRECTORY) { | ||||
|                 let dir = iter.get_directory(); | ||||
|  | ||||
|                 if (folderCategories.indexOf(dir.get_menu_id()) != -1) | ||||
|                     view.addFolder(dir); | ||||
|                 else | ||||
|                     _loadCategory(dir, view); | ||||
|             } | ||||
|         } | ||||
|         view.loadGrid(); | ||||
|  | ||||
|         if (this._focusDummy) { | ||||
|             let focused = this._focusDummy.has_key_focus(); | ||||
|             this._focusDummy.destroy(); | ||||
|             this._focusDummy = null; | ||||
|             if (focused) | ||||
|                 this.actor.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     selectApp: function(id) { | ||||
|         this._showView(Views.ALL); | ||||
|         this._views[Views.ALL].view.selectApp(id); | ||||
| @@ -889,11 +869,24 @@ const AppSearchProvider = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     getInitialResultSet: function(terms, callback, cancellable) { | ||||
|         callback(this._appSys.initial_search(terms)); | ||||
|         let query = terms.join(' '); | ||||
|         let groups = Gio.DesktopAppInfo.search(query); | ||||
|         let usage = Shell.AppUsage.get_default(); | ||||
|         let results = []; | ||||
|         groups.forEach(function(group) { | ||||
|             group = group.filter(function(appID) { | ||||
|                 let app = Gio.DesktopAppInfo.new(appID); | ||||
|                 return app && app.should_show(); | ||||
|             }); | ||||
|             results = results.concat(group.sort(function(a, b) { | ||||
|                 return usage.compare('', a, b); | ||||
|             })); | ||||
|         }); | ||||
|         callback(results); | ||||
|     }, | ||||
|  | ||||
|     getSubsearchResultSet: function(previousResults, terms, callback, cancellable) { | ||||
|         callback(this._appSys.subsearch(previousResults, terms)); | ||||
|         this.getInitialResultSet(terms, callback, cancellable); | ||||
|     }, | ||||
|  | ||||
|     activateResult: function(result) { | ||||
| @@ -943,36 +936,27 @@ const FolderView = new Lang.Class({ | ||||
|         this.actor.add_action(action); | ||||
|     }, | ||||
|  | ||||
|     _getItemId: function(item) { | ||||
|         return item.get_id(); | ||||
|     }, | ||||
|  | ||||
|     _createItemIcon: function(item) { | ||||
|         return new AppIcon(item); | ||||
|     }, | ||||
|  | ||||
|     _compareItems: function(a, b) { | ||||
|         return a.compare_by_name(b); | ||||
|     }, | ||||
|  | ||||
|     addApp: function(app) { | ||||
|         this._addItem(app); | ||||
|     _keyFocusIn: function(actor) { | ||||
|         Util.ensureActorVisibleInScrollView(this.actor, actor); | ||||
|     }, | ||||
|  | ||||
|     createFolderIcon: function(size) { | ||||
|         let icon = new St.Widget({ layout_manager: new Clutter.BinLayout(), | ||||
|                                    style_class: 'app-folder-icon', | ||||
|                                    width: size, height: size }); | ||||
|         let layout = new Clutter.TableLayout(); | ||||
|         let icon = new St.Widget({ layout_manager: layout, | ||||
|                                    style_class: 'app-folder-icon' }); | ||||
|         layout.hookup_style(icon); | ||||
|         let subSize = Math.floor(FOLDER_SUBICON_FRACTION * size); | ||||
|  | ||||
|         let aligns = [ Clutter.ActorAlign.START, Clutter.ActorAlign.END ]; | ||||
|         for (let i = 0; i < Math.min(this._allItems.length, 4); i++) { | ||||
|             let texture = this._allItems[i].create_icon_texture(subSize); | ||||
|             let bin = new St.Bin({ child: texture, | ||||
|                                    x_expand: true, y_expand: true }); | ||||
|             bin.set_x_align(aligns[i % 2]); | ||||
|             bin.set_y_align(aligns[Math.floor(i / 2)]); | ||||
|             icon.add_actor(bin); | ||||
|         let numItems = this._allItems.length; | ||||
|         for (let i = 0; i < 4; i++) { | ||||
|             let bin; | ||||
|             if (i < numItems) { | ||||
|                 let texture = this._allItems[i].app.create_icon_texture(subSize); | ||||
|                 bin = new St.Bin({ child: texture }); | ||||
|             } else { | ||||
|                 bin = new St.Bin({ width: subSize, height: subSize }); | ||||
|             } | ||||
|             layout.pack(bin, i % 2, Math.floor(i / 2)); | ||||
|         } | ||||
|  | ||||
|         return icon; | ||||
| @@ -1044,10 +1028,12 @@ const FolderView = new Lang.Class({ | ||||
| const FolderIcon = new Lang.Class({ | ||||
|     Name: 'FolderIcon', | ||||
|  | ||||
|     _init: function(dir, parentView) { | ||||
|         this._dir = dir; | ||||
|     _init: function(id, path, parentView) { | ||||
|         this.id = id; | ||||
|         this._parentView = parentView; | ||||
|  | ||||
|         this._folder = new Gio.Settings({ schema_id: 'org.gnome.desktop.app-folders.folder', | ||||
|                                           path: path }); | ||||
|         this.actor = new St.Button({ style_class: 'app-well-app app-folder', | ||||
|                                      button_mask: St.ButtonMask.ONE, | ||||
|                                      toggle_mode: true, | ||||
| @@ -1058,15 +1044,11 @@ const FolderIcon = new Lang.Class({ | ||||
|         // whether we need to update arrow side, position etc. | ||||
|         this._popupInvalidated = false; | ||||
|  | ||||
|         let label = this._dir.get_name(); | ||||
|         this.icon = new IconGrid.BaseIcon(label, | ||||
|                                           { createIcon: Lang.bind(this, this._createIcon), setSizeManually: true }); | ||||
|         this.icon = new IconGrid.BaseIcon('', { createIcon: Lang.bind(this, this._createIcon), setSizeManually: true }); | ||||
|         this.actor.set_child(this.icon.actor); | ||||
|         this.actor.label_actor = this.icon.label; | ||||
|  | ||||
|         this.view = new FolderView(); | ||||
|         _loadCategory(dir, this.view); | ||||
|         this.view.loadGrid(); | ||||
|  | ||||
|         this.actor.connect('clicked', Lang.bind(this, | ||||
|             function() { | ||||
| @@ -1079,6 +1061,63 @@ const FolderIcon = new Lang.Class({ | ||||
|                 if (!this.actor.mapped && this._popup) | ||||
|                     this._popup.popdown(); | ||||
|             })); | ||||
|  | ||||
|         this._folder.connect('changed', Lang.bind(this, this._redisplay)); | ||||
|         this._redisplay(); | ||||
|     }, | ||||
|  | ||||
|     getAppIds: function() { | ||||
|         return this.view.getAllItems().map(function(item) { | ||||
|             return item.id; | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     _updateName: function() { | ||||
|         let name = _getFolderName(this._folder); | ||||
|         if (this.name == name) | ||||
|             return; | ||||
|  | ||||
|         this.name = name; | ||||
|         this.icon.label.text = this.name; | ||||
|         this.emit('name-changed'); | ||||
|     }, | ||||
|  | ||||
|     _redisplay: function() { | ||||
|         this._updateName(); | ||||
|  | ||||
|         this.view.removeAll(); | ||||
|  | ||||
|         let excludedApps = this._folder.get_strv('excluded-apps'); | ||||
|         let appSys = Shell.AppSystem.get_default(); | ||||
|         let addAppId = (function addAppId(appId) { | ||||
|             if (excludedApps.indexOf(appId) >= 0) | ||||
|                 return; | ||||
|  | ||||
|             let app = appSys.lookup_app(appId); | ||||
|             if (!app) | ||||
|                 return; | ||||
|  | ||||
|             if (!app.get_app_info().should_show()) | ||||
|                 return; | ||||
|  | ||||
|             let icon = new AppIcon(app); | ||||
|             this.view.addItem(icon); | ||||
|         }).bind(this); | ||||
|  | ||||
|         let folderApps = this._folder.get_strv('apps'); | ||||
|         folderApps.forEach(addAppId); | ||||
|  | ||||
|         let folderCategories = this._folder.get_strv('categories'); | ||||
|         Gio.AppInfo.get_all().forEach(function(appInfo) { | ||||
|             let appCategories = _getCategories(appInfo); | ||||
|             if (!_listsIntersect(folderCategories, appCategories)) | ||||
|                 return; | ||||
|  | ||||
|             addAppId(appInfo.get_id()); | ||||
|         }); | ||||
|  | ||||
|         this.view.loadGrid(); | ||||
|         this.emit('apps-changed'); | ||||
|     }, | ||||
|  | ||||
|     _createIcon: function(iconSize) { | ||||
| @@ -1157,6 +1196,7 @@ const FolderIcon = new Lang.Class({ | ||||
|         this._popupInvalidated = true; | ||||
|     }, | ||||
| }); | ||||
| Signals.addSignalMethods(FolderIcon.prototype); | ||||
|  | ||||
| const AppFolderPopup = new Lang.Class({ | ||||
|     Name: 'AppFolderPopup', | ||||
| @@ -1210,13 +1250,13 @@ const AppFolderPopup = new Lang.Class({ | ||||
|  | ||||
|     _onKeyPress: function(actor, event) { | ||||
|         if (!this._isOpen) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (event.get_key_symbol() != Clutter.KEY_Escape) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         this.popdown(); | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     toggle: function() { | ||||
| @@ -1275,6 +1315,9 @@ const AppIcon = new Lang.Class({ | ||||
|  | ||||
|     _init : function(app, iconParams) { | ||||
|         this.app = app; | ||||
|         this.id = app.get_id(); | ||||
|         this.name = app.get_name(); | ||||
|  | ||||
|         this.actor = new St.Button({ style_class: 'app-well-app', | ||||
|                                      reactive: true, | ||||
|                                      button_mask: St.ButtonMask.ONE | St.ButtonMask.TWO, | ||||
| @@ -1357,13 +1400,13 @@ const AppIcon = new Lang.Class({ | ||||
|                 Lang.bind(this, function() { | ||||
|                     this._menuTimeoutId = 0; | ||||
|                     this.popupMenu(); | ||||
|                     return false; | ||||
|                     return GLib.SOURCE_REMOVE; | ||||
|                 })); | ||||
|         } else if (button == 3) { | ||||
|             this.popupMenu(); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onClicked: function(actor, button) { | ||||
| @@ -1372,10 +1415,6 @@ const AppIcon = new Lang.Class({ | ||||
|         if (button == 1) { | ||||
|             this._onActivate(Clutter.get_current_event()); | ||||
|         } else if (button == 2) { | ||||
|             // Last workspace is always empty | ||||
|             let launchWorkspace = global.screen.get_workspace_by_index(global.screen.n_workspaces - 1); | ||||
|             launchWorkspace.activate(global.get_current_time()); | ||||
|             this.emit('launching'); | ||||
|             this.app.open_new_window(-1); | ||||
|             Main.overview.hide(); | ||||
|         } | ||||
| @@ -1434,7 +1473,6 @@ const AppIcon = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onActivate: function (event) { | ||||
|         this.emit('launching'); | ||||
|         let modifiers = event.get_state(); | ||||
|  | ||||
|         if (modifiers & Clutter.ModifierType.CONTROL_MASK | ||||
| @@ -1486,8 +1524,6 @@ const AppIconMenu = new Lang.Class({ | ||||
|  | ||||
|         this._source = source; | ||||
|  | ||||
|         this.connect('activate', Lang.bind(this, this._onActivate)); | ||||
|  | ||||
|         this.actor.add_style_class_name('app-well-menu'); | ||||
|  | ||||
|         // Chain our visibility and lifecycle to that of the source | ||||
| @@ -1503,7 +1539,9 @@ const AppIconMenu = new Lang.Class({ | ||||
|     _redisplay: function() { | ||||
|         this.removeAll(); | ||||
|  | ||||
|         let windows = this._source.app.get_windows(); | ||||
|         let windows = this._source.app.get_windows().filter(function(w) { | ||||
|             return !w.skip_taskbar; | ||||
|         }); | ||||
|  | ||||
|         // Display the app windows menu items and the separator between windows | ||||
|         // of the current desktop and other windows. | ||||
| @@ -1511,25 +1549,54 @@ const AppIconMenu = new Lang.Class({ | ||||
|         let separatorShown = windows.length > 0 && windows[0].get_workspace() != activeWorkspace; | ||||
|  | ||||
|         for (let i = 0; i < windows.length; i++) { | ||||
|             if (!separatorShown && windows[i].get_workspace() != activeWorkspace) { | ||||
|             let window = windows[i]; | ||||
|             if (!separatorShown && window.get_workspace() != activeWorkspace) { | ||||
|                 this._appendSeparator(); | ||||
|                 separatorShown = true; | ||||
|             } | ||||
|             let item = this._appendMenuItem(windows[i].title); | ||||
|             item._window = windows[i]; | ||||
|             let item = this._appendMenuItem(window.title); | ||||
|             item.connect('activate', Lang.bind(this, function() { | ||||
|                 this.emit('activate-window', window); | ||||
|             })); | ||||
|         } | ||||
|  | ||||
|         if (!this._source.app.is_window_backed()) { | ||||
|             if (windows.length > 0) | ||||
|                 this._appendSeparator(); | ||||
|             this._appendSeparator(); | ||||
|  | ||||
|             this._newWindowMenuItem = this._appendMenuItem(_("New Window")); | ||||
|             this._newWindowMenuItem.connect('activate', Lang.bind(this, function() { | ||||
|                 this._source.app.open_new_window(-1); | ||||
|                 this.emit('activate-window', null); | ||||
|             })); | ||||
|             this._appendSeparator(); | ||||
|  | ||||
|             let appInfo = this._source.app.get_app_info(); | ||||
|             let actions = appInfo.list_actions(); | ||||
|             for (let i = 0; i < actions.length; i++) { | ||||
|                 let action = actions[i]; | ||||
|                 let item = this._appendMenuItem(appInfo.get_action_name(action)); | ||||
|                 item.connect('activate', Lang.bind(this, function(emitter, event) { | ||||
|                     this._source.app.launch_action(action, event.get_time(), -1); | ||||
|                     this.emit('activate-window', null); | ||||
|                 })); | ||||
|             } | ||||
|             this._appendSeparator(); | ||||
|  | ||||
|             let isFavorite = AppFavorites.getAppFavorites().isFavorite(this._source.app.get_id()); | ||||
|  | ||||
|             this._newWindowMenuItem = this._appendMenuItem(_("New Window")); | ||||
|             this._appendSeparator(); | ||||
|  | ||||
|             this._toggleFavoriteMenuItem = this._appendMenuItem(isFavorite ? _("Remove from Favorites") | ||||
|                                                                 : _("Add to Favorites")); | ||||
|             if (isFavorite) { | ||||
|                 let item = this._appendMenuItem(_("Remove from Favorites")); | ||||
|                 item.connect('activate', Lang.bind(this, function() { | ||||
|                     let favs = AppFavorites.getAppFavorites(); | ||||
|                     favs.removeFavorite(this._source.app.get_id()); | ||||
|                 })); | ||||
|             } else { | ||||
|                 let item = this._appendMenuItem(_("Add to Favorites")); | ||||
|                 item.connect('activate', Lang.bind(this, function() { | ||||
|                     let favs = AppFavorites.getAppFavorites(); | ||||
|                     favs.addFavorite(this._source.app.get_id()); | ||||
|                 })); | ||||
|             } | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -1548,24 +1615,6 @@ const AppIconMenu = new Lang.Class({ | ||||
|     popup: function(activatingButton) { | ||||
|         this._redisplay(); | ||||
|         this.open(); | ||||
|     }, | ||||
|  | ||||
|     _onActivate: function (actor, child) { | ||||
|         if (child._window) { | ||||
|             let metaWindow = child._window; | ||||
|             this.emit('activate-window', metaWindow); | ||||
|         } else if (child == this._newWindowMenuItem) { | ||||
|             this._source.app.open_new_window(-1); | ||||
|             this.emit('activate-window', null); | ||||
|         } else if (child == this._toggleFavoriteMenuItem) { | ||||
|             let favs = AppFavorites.getAppFavorites(); | ||||
|             let isFavorite = favs.isFavorite(this._source.app.get_id()); | ||||
|             if (isFavorite) | ||||
|                 favs.removeFavorite(this._source.app.get_id()); | ||||
|             else | ||||
|                 favs.addFavorite(this._source.app.get_id()); | ||||
|         } | ||||
|         this.close(); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(AppIconMenu.prototype); | ||||
|   | ||||
| @@ -50,11 +50,9 @@ const BackgroundCache = new Lang.Class({ | ||||
|                                         effects: Meta.BackgroundEffects.NONE }); | ||||
|  | ||||
|         let content = null; | ||||
|  | ||||
|         let candidateContent = null; | ||||
|         for (let i = 0; i < this._patterns.length; i++) { | ||||
|             if (!this._patterns[i]) | ||||
|                 continue; | ||||
|  | ||||
|             if (this._patterns[i].get_shading() != params.shadingType) | ||||
|                 continue; | ||||
|  | ||||
| @@ -88,7 +86,6 @@ const BackgroundCache = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         this._patterns.push(content); | ||||
|  | ||||
|         return content; | ||||
|     }, | ||||
|  | ||||
| @@ -116,9 +113,9 @@ const BackgroundCache = new Lang.Class({ | ||||
|  | ||||
|     _removeContent: function(contentList, content) { | ||||
|         let index = contentList.indexOf(content); | ||||
|  | ||||
|         if (index >= 0) | ||||
|             contentList.splice(index, 1); | ||||
|         if (index < 0) | ||||
|             throw new Error("Trying to remove invalid content: " + content); | ||||
|         contentList.splice(index, 1); | ||||
|     }, | ||||
|  | ||||
|     removePatternContent: function(content) { | ||||
| @@ -128,12 +125,32 @@ const BackgroundCache = new Lang.Class({ | ||||
|     removeImageContent: function(content) { | ||||
|         let filename = content.get_filename(); | ||||
|  | ||||
|         if (filename && this._fileMonitors[filename]) | ||||
|         let hasOtherUsers = this._images.some(function(content) { return filename == content.get_filename(); }); | ||||
|         if (!hasOtherUsers) | ||||
|             delete this._fileMonitors[filename]; | ||||
|  | ||||
|         this._removeContent(this._images, content); | ||||
|     }, | ||||
|  | ||||
|     _attachCallerToFileLoad: function(caller, fileLoad) { | ||||
|         fileLoad.callers.push(caller); | ||||
|  | ||||
|         if (!caller.cancellable) | ||||
|             return; | ||||
|  | ||||
|         caller.cancellable.connect(Lang.bind(this, function() { | ||||
|             let idx = fileLoad.callers.indexOf(caller); | ||||
|             fileLoad.callers.splice(idx, 1); | ||||
|  | ||||
|             if (fileLoad.callers.length == 0) { | ||||
|                 fileLoad.cancellable.cancel(); | ||||
|  | ||||
|                 let idx = this._pendingFileLoads.indexOf(fileLoad); | ||||
|                 this._pendingFileLoads.splice(idx, 1); | ||||
|             } | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     _loadImageContent: function(params) { | ||||
|         params = Params.parse(params, { monitorIndex: 0, | ||||
|                                         style: null, | ||||
| @@ -142,27 +159,28 @@ const BackgroundCache = new Lang.Class({ | ||||
|                                         cancellable: null, | ||||
|                                         onFinished: null }); | ||||
|  | ||||
|         let caller = { monitorIndex: params.monitorIndex, | ||||
|                        effects: params.effects, | ||||
|                        cancellable: params.cancellable, | ||||
|                        onFinished: params.onFinished }; | ||||
|  | ||||
|         for (let i = 0; i < this._pendingFileLoads.length; i++) { | ||||
|             if (this._pendingFileLoads[i].filename == params.filename && | ||||
|                 this._pendingFileLoads[i].style == params.style) { | ||||
|                 this._pendingFileLoads[i].callers.push({ shouldCopy: true, | ||||
|                                                          monitorIndex: params.monitorIndex, | ||||
|                                                          effects: params.effects, | ||||
|                                                          onFinished: params.onFinished }); | ||||
|             let fileLoad = this._pendingFileLoads[i]; | ||||
|  | ||||
|             if (fileLoad.filename == params.filename && | ||||
|                 fileLoad.style == params.style) { | ||||
|                 this._attachCallerToFileLoad(caller, fileLoad); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this._pendingFileLoads.push({ filename: params.filename, | ||||
|                                       style: params.style, | ||||
|                                       callers: [{ shouldCopy: false, | ||||
|                                                   monitorIndex: params.monitorIndex, | ||||
|                                                   effects: params.effects, | ||||
|                                                   onFinished: params.onFinished }] }); | ||||
|         let fileLoad = { filename: params.filename, | ||||
|                          style: params.style, | ||||
|                          cancellable: new Gio.Cancellable(), | ||||
|                          callers: [] }; | ||||
|         this._attachCallerToFileLoad(caller, fileLoad); | ||||
|  | ||||
|         let content = new Meta.Background({ meta_screen: global.screen, | ||||
|                                             monitor: params.monitorIndex, | ||||
|                                             effects: params.effects }); | ||||
|         let content = new Meta.Background({ meta_screen: global.screen }); | ||||
|  | ||||
|         content.load_file_async(params.filename, | ||||
|                                 params.style, | ||||
| @@ -173,31 +191,26 @@ const BackgroundCache = new Lang.Class({ | ||||
|                                                   content.load_file_finish(result); | ||||
|  | ||||
|                                                   this._monitorFile(params.filename); | ||||
|                                                   this._images.push(content); | ||||
|                                               } catch(e) { | ||||
|                                                   content = null; | ||||
|                                               } | ||||
|  | ||||
|                                               for (let i = 0; i < this._pendingFileLoads.length; i++) { | ||||
|                                                   let pendingLoad = this._pendingFileLoads[i]; | ||||
|                                                   if (pendingLoad.filename != params.filename || | ||||
|                                                       pendingLoad.style != params.style) | ||||
|                                                       continue; | ||||
|                                               for (let i = 0; i < fileLoad.callers.length; i++) { | ||||
|                                                   let caller = fileLoad.callers[i]; | ||||
|                                                   if (caller.onFinished) { | ||||
|                                                       let newContent; | ||||
|  | ||||
|                                                   for (let j = 0; j < pendingLoad.callers.length; j++) { | ||||
|                                                       if (pendingLoad.callers[j].onFinished) { | ||||
|                                                           if (content && pendingLoad.callers[j].shouldCopy) { | ||||
|                                                               content = object.copy(pendingLoad.callers[j].monitorIndex, | ||||
|                                                                                     pendingLoad.callers[j].effects); | ||||
|  | ||||
|                                                           } | ||||
|  | ||||
|                                                           pendingLoad.callers[j].onFinished(content); | ||||
|                                                       if (content) { | ||||
|                                                           newContent = content.copy(caller.monitorIndex, caller.effects); | ||||
|                                                           this._images.push(newContent); | ||||
|                                                       } | ||||
|                                                   } | ||||
|  | ||||
|                                                   this._pendingFileLoads.splice(i, 1); | ||||
|                                                       caller.onFinished(newContent); | ||||
|                                                   } | ||||
|                                               } | ||||
|  | ||||
|                                               let idx = this._pendingFileLoads.indexOf(fileLoad); | ||||
|                                               this._pendingFileLoads.splice(idx, 1); | ||||
|                                           })); | ||||
|     }, | ||||
|  | ||||
| @@ -210,11 +223,9 @@ const BackgroundCache = new Lang.Class({ | ||||
|                                         onFinished: null }); | ||||
|  | ||||
|         let content = null; | ||||
|  | ||||
|         let candidateContent = null; | ||||
|         for (let i = 0; i < this._images.length; i++) { | ||||
|             if (!this._images[i]) | ||||
|                 continue; | ||||
|  | ||||
|             if (this._images[i].get_style() != params.style) | ||||
|                 continue; | ||||
|  | ||||
| @@ -222,7 +233,7 @@ const BackgroundCache = new Lang.Class({ | ||||
|                 continue; | ||||
|  | ||||
|             if (params.style == GDesktopEnums.BackgroundStyle.SPANNED && | ||||
|                 this._images[i].monitor_index != this._monitorIndex) | ||||
|                 this._images[i].monitor != params.monitorIndex) | ||||
|                 continue; | ||||
|  | ||||
|             candidateContent = this._images[i]; | ||||
| @@ -262,6 +273,7 @@ const BackgroundCache = new Lang.Class({ | ||||
|             if (params.onLoaded) { | ||||
|                 GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { | ||||
|                     params.onLoaded(this._animation); | ||||
|                     return GLib.SOURCE_REMOVE; | ||||
|                 })); | ||||
|             } | ||||
|         } | ||||
| @@ -276,6 +288,7 @@ const BackgroundCache = new Lang.Class({ | ||||
|                            if (params.onLoaded) { | ||||
|                                GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { | ||||
|                                    params.onLoaded(this._animation); | ||||
|                                    return GLib.SOURCE_REMOVE; | ||||
|                                })); | ||||
|                            } | ||||
|                        })); | ||||
| @@ -375,7 +388,7 @@ const Background = new Lang.Class({ | ||||
|  | ||||
|         GLib.idle_add(GLib.PRIORITY_DEFAULT, Lang.bind(this, function() { | ||||
|             this.emit('loaded'); | ||||
|             return false; | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
| @@ -414,27 +427,26 @@ const Background = new Lang.Class({ | ||||
|         this._fileWatches[filename] = signalId; | ||||
|     }, | ||||
|  | ||||
|     _addImage: function(content, index, filename) { | ||||
|         content.brightness = this._brightness; | ||||
|         content.vignette_sharpness = this._vignetteSharpness; | ||||
|     _ensureImage: function(index) { | ||||
|         if (this._images[index]) | ||||
|             return; | ||||
|  | ||||
|         let actor = new Meta.BackgroundActor(); | ||||
|         actor.content = content; | ||||
|  | ||||
|         // The background pattern is the first actor in | ||||
|         // the group, and all images should be above that. | ||||
|         this.actor.insert_child_at_index(actor, index + 1); | ||||
|  | ||||
|         this._images[index] = actor; | ||||
|         this._watchCacheFile(filename); | ||||
|     }, | ||||
|  | ||||
|     _updateImage: function(content, index, filename) { | ||||
|     _updateImage: function(index, content, filename) { | ||||
|         content.brightness = this._brightness; | ||||
|         content.vignette_sharpness = this._vignetteSharpness; | ||||
|  | ||||
|         this._cache.removeImageContent(this._images[index].content); | ||||
|         this._images[index].content = content; | ||||
|         let image = this._images[index]; | ||||
|         if (image.content) | ||||
|             this._cache.removeImageContent(content); | ||||
|         image.content = content; | ||||
|         this._watchCacheFile(filename); | ||||
|     }, | ||||
|  | ||||
| @@ -482,11 +494,8 @@ const Background = new Lang.Class({ | ||||
|                                                   return; | ||||
|                                               } | ||||
|  | ||||
|                                               if (!this._images[i]) { | ||||
|                                                   this._addImage(content, i, files[i]); | ||||
|                                               } else { | ||||
|                                                   this._updateImage(content, i, files[i]); | ||||
|                                               } | ||||
|                                               this._ensureImage(i); | ||||
|                                               this._updateImage(i, content, files[i]); | ||||
|  | ||||
|                                               if (numPendingImages == 0) { | ||||
|                                                   this._setLoaded(); | ||||
| @@ -521,7 +530,7 @@ const Background = new Lang.Class({ | ||||
|                                                       Lang.bind(this, function() { | ||||
|                                                                     this._updateAnimationTimeoutId = 0; | ||||
|                                                                     this._updateAnimation(); | ||||
|                                                                     return false; | ||||
|                                                                     return GLib.SOURCE_REMOVE; | ||||
|                                                                 })); | ||||
|     }, | ||||
|  | ||||
| @@ -541,30 +550,33 @@ const Background = new Lang.Class({ | ||||
|                                            }); | ||||
|     }, | ||||
|  | ||||
|     _loadFile: function(filename) { | ||||
|     _loadImage: function(filename) { | ||||
|         this._cache.getImageContent({ monitorIndex: this._monitorIndex, | ||||
|                                       effects: this._effects, | ||||
|                                       style: this._style, | ||||
|                                       filename: filename, | ||||
|                                       cancellable: this._cancellable, | ||||
|                                       onFinished: Lang.bind(this, function(content) { | ||||
|                                           if (!content) { | ||||
|                                               if (!this._cancellable.is_cancelled()) | ||||
|                                                   this._loadAnimation(filename); | ||||
|                                               return; | ||||
|                                           if (content) { | ||||
|                                               this._ensureImage(0); | ||||
|                                               this._updateImage(0, content, filename); | ||||
|                                           } | ||||
|  | ||||
|                                           this._addImage(content, 0, filename); | ||||
|                                           this._setLoaded(); | ||||
|                                       }) | ||||
|                                     }); | ||||
|     }, | ||||
|  | ||||
|     _loadFile: function(filename) { | ||||
|         if (filename.endsWith('.xml')) | ||||
|             this._loadAnimation(filename); | ||||
|         else | ||||
|             this._loadImage(filename); | ||||
|     }, | ||||
|  | ||||
|     _load: function () { | ||||
|         this._cache = getBackgroundCache(); | ||||
|  | ||||
|         this._loadPattern(this._cache); | ||||
|         this._loadPattern(); | ||||
|  | ||||
|         this._style = this._settings.get_enum(BACKGROUND_STYLE_KEY); | ||||
|         if (this._style == GDesktopEnums.BackgroundStyle.NONE) { | ||||
| @@ -638,7 +650,13 @@ const SystemBackground = new Lang.Class({ | ||||
|                                           this.emit('loaded'); | ||||
|                                       }) | ||||
|                                     }); | ||||
|     } | ||||
|  | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
|         this._cache.removeImageContent(this.actor.content); | ||||
|     }, | ||||
| }); | ||||
| Signals.addSignalMethods(SystemBackground.prototype); | ||||
|  | ||||
| @@ -726,29 +744,31 @@ const BackgroundManager = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateBackground: function(background, monitorIndex) { | ||||
|         let newBackground = this._createBackground(monitorIndex); | ||||
|         newBackground.vignetteSharpness = background.vignetteSharpness; | ||||
|         newBackground.brightness = background.brightness; | ||||
|         newBackground.visible = background.visible; | ||||
|     _updateBackground: function() { | ||||
|         let newBackground = this._createBackground(); | ||||
|         newBackground.vignetteSharpness = this.background.vignetteSharpness; | ||||
|         newBackground.brightness = this.background.brightness; | ||||
|         newBackground.visible = this.background.visible; | ||||
|  | ||||
|         newBackground.loadedSignalId = newBackground.connect('loaded', | ||||
|             Lang.bind(this, function() { | ||||
|                 newBackground.disconnect(newBackground.loadedSignalId); | ||||
|                 newBackground.loadedSignalId = 0; | ||||
|                 Tweener.addTween(background.actor, | ||||
|  | ||||
|                 if (this._newBackground != newBackground) { | ||||
|                     /* Not interesting, we queued another load */ | ||||
|                     newBackground.actor.destroy(); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 Tweener.addTween(this.background.actor, | ||||
|                                  { opacity: 0, | ||||
|                                    time: FADE_ANIMATION_TIME, | ||||
|                                    transition: 'easeOutQuad', | ||||
|                                    onComplete: Lang.bind(this, function() { | ||||
|                                        if (this._newBackground == newBackground) { | ||||
|                                            this.background = newBackground; | ||||
|                                            this._newBackground = null; | ||||
|                                        } else { | ||||
|                                            newBackground.actor.destroy(); | ||||
|                                        } | ||||
|  | ||||
|                                        background.actor.destroy(); | ||||
|                                        this.background.actor.destroy(); | ||||
|                                        this.background = newBackground; | ||||
|                                        this._newBackground = null; | ||||
|  | ||||
|                                        this.emit('changed'); | ||||
|                                    }) | ||||
| @@ -776,7 +796,7 @@ const BackgroundManager = new Lang.Class({ | ||||
|         background.changeSignalId = background.connect('changed', Lang.bind(this, function() { | ||||
|             background.disconnect(background.changeSignalId); | ||||
|             background.changeSignalId = 0; | ||||
|             this._updateBackground(background, this._monitorIndex); | ||||
|             this._updateBackground(); | ||||
|         })); | ||||
|  | ||||
|         background.actor.connect('destroy', Lang.bind(this, function() { | ||||
|   | ||||
| @@ -13,8 +13,8 @@ const BackgroundMenu = new Lang.Class({ | ||||
|     Name: 'BackgroundMenu', | ||||
|     Extends: PopupMenu.PopupMenu, | ||||
|  | ||||
|     _init: function(source) { | ||||
|         this.parent(source, 0, St.Side.TOP); | ||||
|     _init: function(layoutManager) { | ||||
|         this.parent(layoutManager.dummyCursor, 0, St.Side.TOP); | ||||
|  | ||||
|         this.addSettingsAction(_("Settings"), 'gnome-control-center.desktop'); | ||||
|         this.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
| @@ -22,23 +22,20 @@ const BackgroundMenu = new Lang.Class({ | ||||
|  | ||||
|         this.actor.add_style_class_name('background-menu'); | ||||
|  | ||||
|         Main.uiGroup.add_actor(this.actor); | ||||
|         layoutManager.uiGroup.add_actor(this.actor); | ||||
|         this.actor.hide(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| function addBackgroundMenu(actor) { | ||||
|     let cursor = new St.Bin({ opacity: 0 }); | ||||
|     Main.uiGroup.add_actor(cursor); | ||||
|  | ||||
| function addBackgroundMenu(actor, layoutManager) { | ||||
|     actor.reactive = true; | ||||
|     actor._backgroundMenu = new BackgroundMenu(cursor); | ||||
|     actor._backgroundMenu = new BackgroundMenu(layoutManager); | ||||
|     actor._backgroundManager = new PopupMenu.PopupMenuManager({ actor: actor }); | ||||
|     actor._backgroundManager.addMenu(actor._backgroundMenu); | ||||
|  | ||||
|     function openMenu() { | ||||
|         let [x, y] = global.get_pointer(); | ||||
|         cursor.set_position(x, y); | ||||
|         Main.layoutManager.setDummyCursorGeometry(x, y, 0, 0); | ||||
|         actor._backgroundMenu.open(BoxPointer.PopupAnimation.NONE); | ||||
|     } | ||||
|  | ||||
| @@ -59,10 +56,8 @@ function addBackgroundMenu(actor) { | ||||
|     actor.add_action(clickAction); | ||||
|  | ||||
|     actor.connect('destroy', function() { | ||||
|                       actor._backgroundMenu.destroy(); | ||||
|                       actor._backgroundMenu = null; | ||||
|                       actor._backgroundManager = null; | ||||
|  | ||||
|                       cursor.destroy(); | ||||
|                   }); | ||||
|         actor._backgroundMenu.destroy(); | ||||
|         actor._backgroundMenu = null; | ||||
|         actor._backgroundManager = null; | ||||
|     }); | ||||
| } | ||||
|   | ||||
| @@ -69,7 +69,7 @@ const BoxPointer = new Lang.Class({ | ||||
|     _muteInput: function() { | ||||
|         if (this._capturedEventId == 0) | ||||
|             this._capturedEventId = this.actor.connect('captured-event', | ||||
|                                                        function() { return true; }); | ||||
|                                                        function() { return Clutter.EVENT_STOP; }); | ||||
|     }, | ||||
|  | ||||
|     _unmuteInput: function() { | ||||
| @@ -121,6 +121,9 @@ const BoxPointer = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     hide: function(animate, onComplete) { | ||||
|         if (!this.actor.visible) | ||||
|             return; | ||||
|  | ||||
|         let xOffset = 0; | ||||
|         let yOffset = 0; | ||||
|         let themeNode = this.actor.get_theme_node(); | ||||
| @@ -284,38 +287,40 @@ const BoxPointer = new Lang.Class({ | ||||
|         let skipBottomLeft = false; | ||||
|         let skipBottomRight = false; | ||||
|  | ||||
|         switch (this._arrowSide) { | ||||
|         case St.Side.TOP: | ||||
|             if (this._arrowOrigin == x1) | ||||
|                 skipTopLeft = true; | ||||
|             else if (this._arrowOrigin == x2) | ||||
|                 skipTopRight = true; | ||||
|             break; | ||||
|         if (rise) { | ||||
|             switch (this._arrowSide) { | ||||
|             case St.Side.TOP: | ||||
|                 if (this._arrowOrigin == x1) | ||||
|                     skipTopLeft = true; | ||||
|                 else if (this._arrowOrigin == x2) | ||||
|                     skipTopRight = true; | ||||
|                 break; | ||||
|  | ||||
|         case St.Side.RIGHT: | ||||
|             if (this._arrowOrigin == y1) | ||||
|                 skipTopRight = true; | ||||
|             else if (this._arrowOrigin == y2) | ||||
|                 skipBottomRight = true; | ||||
|             break; | ||||
|             case St.Side.RIGHT: | ||||
|                 if (this._arrowOrigin == y1) | ||||
|                     skipTopRight = true; | ||||
|                 else if (this._arrowOrigin == y2) | ||||
|                     skipBottomRight = true; | ||||
|                 break; | ||||
|  | ||||
|         case St.Side.BOTTOM: | ||||
|             if (this._arrowOrigin == x1) | ||||
|                 skipBottomLeft = true; | ||||
|             else if (this._arrowOrigin == x2) | ||||
|                 skipBottomRight = true; | ||||
|             break; | ||||
|             case St.Side.BOTTOM: | ||||
|                 if (this._arrowOrigin == x1) | ||||
|                     skipBottomLeft = true; | ||||
|                 else if (this._arrowOrigin == x2) | ||||
|                     skipBottomRight = true; | ||||
|                 break; | ||||
|  | ||||
|         case St.Side.LEFT: | ||||
|             if (this._arrowOrigin == y1) | ||||
|                 skipTopLeft = true; | ||||
|             else if (this._arrowOrigin == y2) | ||||
|                 skipBottomLeft = true; | ||||
|             break; | ||||
|             case St.Side.LEFT: | ||||
|                 if (this._arrowOrigin == y1) | ||||
|                     skipTopLeft = true; | ||||
|                 else if (this._arrowOrigin == y2) | ||||
|                     skipBottomLeft = true; | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         cr.moveTo(x1 + borderRadius, y1); | ||||
|         if (this._arrowSide == St.Side.TOP) { | ||||
|         if (this._arrowSide == St.Side.TOP && rise) { | ||||
|             if (skipTopLeft) { | ||||
|                 cr.moveTo(x1, y2 - borderRadius); | ||||
|                 cr.lineTo(x1, y1 - rise); | ||||
| @@ -337,7 +342,7 @@ const BoxPointer = new Lang.Class({ | ||||
|                    3*Math.PI/2, Math.PI*2); | ||||
|         } | ||||
|  | ||||
|         if (this._arrowSide == St.Side.RIGHT) { | ||||
|         if (this._arrowSide == St.Side.RIGHT && rise) { | ||||
|             if (skipTopRight) { | ||||
|                 cr.lineTo(x2 + rise, y1); | ||||
|                 cr.lineTo(x2 + rise, y1 + halfBase); | ||||
| @@ -358,7 +363,7 @@ const BoxPointer = new Lang.Class({ | ||||
|                    0, Math.PI/2); | ||||
|         } | ||||
|  | ||||
|         if (this._arrowSide == St.Side.BOTTOM) { | ||||
|         if (this._arrowSide == St.Side.BOTTOM && rise) { | ||||
|             if (skipBottomLeft) { | ||||
|                 cr.lineTo(x1 + halfBase, y2); | ||||
|                 cr.lineTo(x1, y2 + rise); | ||||
| @@ -379,7 +384,7 @@ const BoxPointer = new Lang.Class({ | ||||
|                    Math.PI/2, Math.PI); | ||||
|         } | ||||
|  | ||||
|         if (this._arrowSide == St.Side.LEFT) { | ||||
|         if (this._arrowSide == St.Side.LEFT && rise) { | ||||
|             if (skipTopLeft) { | ||||
|                 cr.lineTo(x1, y1 + halfBase); | ||||
|                 cr.lineTo(x1 - rise, y1); | ||||
|   | ||||
| @@ -17,16 +17,18 @@ const SHOW_WEEKDATE_KEY = 'show-weekdate'; | ||||
| // in org.gnome.desktop.interface | ||||
| const CLOCK_FORMAT_KEY        = 'clock-format'; | ||||
|  | ||||
| function _sameDay(dateA, dateB) { | ||||
|     return (dateA.getDate() == dateB.getDate() && | ||||
|             dateA.getMonth() == dateB.getMonth() && | ||||
|             dateA.getYear() == dateB.getYear()); | ||||
| } | ||||
|  | ||||
| function _sameYear(dateA, dateB) { | ||||
|     return (dateA.getYear() == dateB.getYear()); | ||||
| } | ||||
|  | ||||
| function _sameMonth(dateA, dateB) { | ||||
|     return _sameYear(dateA, dateB) && (dateA.getMonth() == dateB.getMonth()); | ||||
| } | ||||
|  | ||||
| function _sameDay(dateA, dateB) { | ||||
|     return _sameMonth(dateA, dateB) && (dateA.getDate() == dateB.getDate()); | ||||
| } | ||||
|  | ||||
| /* TODO: maybe needs config - right now we assume that Saturday and | ||||
|  * Sunday are non-work days (not true in e.g. Israel, it's Sunday and | ||||
|  * Monday there) | ||||
| @@ -329,25 +331,22 @@ const DBusEventSource = new Lang.Class({ | ||||
|             return; | ||||
|  | ||||
|         if (this._curRequestBegin && this._curRequestEnd){ | ||||
|             let callFlags = Gio.DBusCallFlags.NO_AUTO_START; | ||||
|             if (forceReload) | ||||
|                 callFlags = Gio.DBusCallFlags.NONE; | ||||
|             this._dbusProxy.GetEventsRemote(this._curRequestBegin.getTime() / 1000, | ||||
|                                             this._curRequestEnd.getTime() / 1000, | ||||
|                                             forceReload, | ||||
|                                             Lang.bind(this, this._onEventsReceived), | ||||
|                                             callFlags); | ||||
|                                             Gio.DBusCallFlags.NONE); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     requestRange: function(begin, end, forceReload) { | ||||
|         if (forceReload || !(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) { | ||||
|     requestRange: function(begin, end) { | ||||
|         if (!(_datesEqual(begin, this._lastRequestBegin) && _datesEqual(end, this._lastRequestEnd))) { | ||||
|             this.isLoading = true; | ||||
|             this._lastRequestBegin = begin; | ||||
|             this._lastRequestEnd = end; | ||||
|             this._curRequestBegin = begin; | ||||
|             this._curRequestEnd = end; | ||||
|             this._loadEvents(forceReload); | ||||
|             this._loadEvents(false); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -404,6 +403,8 @@ const Calendar = new Lang.Class({ | ||||
|         // Start off with the current date | ||||
|         this._selectedDate = new Date(); | ||||
|  | ||||
|         this._shouldDateGrabFocus = false; | ||||
|  | ||||
|         this.actor = new St.Table({ homogeneous: false, | ||||
|                                     style_class: 'calendar', | ||||
|                                     reactive: true }); | ||||
| @@ -419,21 +420,21 @@ const Calendar = new Lang.Class({ | ||||
|     setEventSource: function(eventSource) { | ||||
|         this._eventSource = eventSource; | ||||
|         this._eventSource.connect('changed', Lang.bind(this, function() { | ||||
|             this._update(false); | ||||
|             this._rebuildCalendar(); | ||||
|             this._update(); | ||||
|         })); | ||||
|         this._update(true); | ||||
|         this._rebuildCalendar(); | ||||
|         this._update(); | ||||
|     }, | ||||
|  | ||||
|     // Sets the calendar to show a specific date | ||||
|     setDate: function(date, forceReload) { | ||||
|         if (!_sameDay(date, this._selectedDate)) { | ||||
|             this._selectedDate = date; | ||||
|             this._update(forceReload); | ||||
|             this.emit('selected-date-changed', new Date(this._selectedDate)); | ||||
|         } else { | ||||
|             if (forceReload) | ||||
|                 this._update(forceReload); | ||||
|         } | ||||
|     setDate: function(date) { | ||||
|         if (_sameDay(date, this._selectedDate)) | ||||
|             return; | ||||
|  | ||||
|         this._selectedDate = date; | ||||
|         this._update(); | ||||
|         this.emit('selected-date-changed', new Date(this._selectedDate)); | ||||
|     }, | ||||
|  | ||||
|     _buildHeader: function() { | ||||
| @@ -497,6 +498,7 @@ const Calendar = new Lang.Class({ | ||||
|             this._onNextMonthButtonClicked(); | ||||
|             break; | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onPrevMonthButtonClicked: function() { | ||||
| @@ -520,7 +522,7 @@ const Calendar = new Lang.Class({ | ||||
|  | ||||
|         this._backButton.grab_key_focus(); | ||||
|  | ||||
|         this.setDate(newDate, false); | ||||
|         this.setDate(newDate); | ||||
|     }, | ||||
|  | ||||
|     _onNextMonthButtonClicked: function() { | ||||
| @@ -544,28 +546,26 @@ const Calendar = new Lang.Class({ | ||||
|  | ||||
|         this._forwardButton.grab_key_focus(); | ||||
|  | ||||
|         this.setDate(newDate, false); | ||||
|         this.setDate(newDate); | ||||
|     }, | ||||
|  | ||||
|     _onSettingsChange: function() { | ||||
|         this._useWeekdate = this._settings.get_boolean(SHOW_WEEKDATE_KEY); | ||||
|         this._buildHeader(); | ||||
|         this._update(false); | ||||
|         this._rebuildCalendar(); | ||||
|         this._update(); | ||||
|     }, | ||||
|  | ||||
|     _update: function(forceReload) { | ||||
|     _rebuildCalendar: function() { | ||||
|         let now = new Date(); | ||||
|  | ||||
|         if (_sameYear(this._selectedDate, now)) | ||||
|             this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear); | ||||
|         else | ||||
|             this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat); | ||||
|  | ||||
|         // Remove everything but the topBox and the weekday labels | ||||
|         let children = this.actor.get_children(); | ||||
|         for (let i = this._firstDayIndex; i < children.length; i++) | ||||
|             children[i].destroy(); | ||||
|  | ||||
|         this._buttons = []; | ||||
|  | ||||
|         // Start at the beginning of the week before the start of the month | ||||
|         // | ||||
|         // We want to show always 6 weeks (to keep the calendar menu at the same | ||||
| @@ -583,11 +583,13 @@ const Calendar = new Lang.Class({ | ||||
|         // Actually computing the number of weeks is complex, but we know that the | ||||
|         // problematic categories (2 and 4) always start on week start, and that | ||||
|         // all months at the end have 6 weeks. | ||||
|  | ||||
|         let beginDate = new Date(this._selectedDate); | ||||
|         beginDate.setDate(1); | ||||
|         beginDate.setSeconds(0); | ||||
|         beginDate.setHours(12); | ||||
|  | ||||
|         this._calendarBegin = new Date(beginDate); | ||||
|  | ||||
|         let year = beginDate.getYear(); | ||||
|  | ||||
|         let daysToWeekStart = (7 + beginDate.getDay() - this._weekStart) % 7; | ||||
| @@ -608,13 +610,10 @@ const Calendar = new Lang.Class({ | ||||
|             if (this._eventSource.isDummy) | ||||
|                 button.reactive = false; | ||||
|  | ||||
|             let iterStr = iter.toUTCString(); | ||||
|             button._date = new Date(iter); | ||||
|             button.connect('clicked', Lang.bind(this, function() { | ||||
|                 this._shouldDateGrabFocus = true; | ||||
|  | ||||
|                 let newlySelectedDate = new Date(iterStr); | ||||
|                 this.setDate(newlySelectedDate, false); | ||||
|  | ||||
|                 this.setDate(button._date); | ||||
|                 this._shouldDateGrabFocus = false; | ||||
|             })); | ||||
|  | ||||
| @@ -622,9 +621,9 @@ const Calendar = new Lang.Class({ | ||||
|             let styleClass = 'calendar-day-base calendar-day'; | ||||
|  | ||||
|             if (_isWorkDay(iter)) | ||||
|                 styleClass += ' calendar-work-day' | ||||
|                 styleClass += ' calendar-work-day'; | ||||
|             else | ||||
|                 styleClass += ' calendar-nonwork-day' | ||||
|                 styleClass += ' calendar-nonwork-day'; | ||||
|  | ||||
|             // Hack used in lieu of border-collapse - see gnome-shell.css | ||||
|             if (row == 2) | ||||
| @@ -641,7 +640,7 @@ const Calendar = new Lang.Class({ | ||||
|                 styleClass += ' calendar-other-month-day'; | ||||
|  | ||||
|             if (hasEvents) | ||||
|                 styleClass += ' calendar-day-with-events' | ||||
|                 styleClass += ' calendar-day-with-events'; | ||||
|  | ||||
|             button.style_class = styleClass; | ||||
|  | ||||
| @@ -649,12 +648,7 @@ const Calendar = new Lang.Class({ | ||||
|             this.actor.add(button, | ||||
|                            { row: row, col: offsetCols + (7 + iter.getDay() - this._weekStart) % 7 }); | ||||
|  | ||||
|             if (_sameDay(this._selectedDate, iter)) { | ||||
|                 button.add_style_pseudo_class('active'); | ||||
|  | ||||
|                 if (this._shouldDateGrabFocus) | ||||
|                     button.grab_key_focus(); | ||||
|             } | ||||
|             this._buttons.push(button); | ||||
|  | ||||
|             if (this._useWeekdate && iter.getDay() == 4) { | ||||
|                 let label = new St.Label({ text: _getCalendarWeekForDate(iter).toString(), | ||||
| @@ -668,9 +662,32 @@ const Calendar = new Lang.Class({ | ||||
|             if (iter.getDay() == this._weekStart) | ||||
|                 row++; | ||||
|         } | ||||
|  | ||||
|         // Signal to the event source that we are interested in events | ||||
|         // only from this date range | ||||
|         this._eventSource.requestRange(beginDate, iter, forceReload); | ||||
|         this._eventSource.requestRange(beginDate, iter); | ||||
|     }, | ||||
|  | ||||
|     _update: function() { | ||||
|         let now = new Date(); | ||||
|  | ||||
|         if (_sameYear(this._selectedDate, now)) | ||||
|             this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormatWithoutYear); | ||||
|         else | ||||
|             this._monthLabel.text = this._selectedDate.toLocaleFormat(this._headerFormat); | ||||
|  | ||||
|         if (!this._calendarBegin || !_sameMonth(this._selectedDate, this._calendarBegin)) | ||||
|             this._rebuildCalendar(); | ||||
|  | ||||
|         this._buttons.forEach(Lang.bind(this, function(button) { | ||||
|             if (_sameDay(button._date, this._selectedDate)) { | ||||
|                 button.add_style_pseudo_class('active'); | ||||
|                 if (this._shouldDateGrabFocus) | ||||
|                     button.grab_key_focus(); | ||||
|             } | ||||
|             else | ||||
|                 button.remove_style_pseudo_class('active'); | ||||
|         })); | ||||
|     } | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -77,7 +77,7 @@ const AutomountManager = new Lang.Class({ | ||||
|         })); | ||||
|  | ||||
|         this._mountAllId = 0; | ||||
|         return false; | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     _onDriveConnected: function() { | ||||
| @@ -236,7 +236,7 @@ const AutomountManager = new Lang.Class({ | ||||
|     _allowAutorunExpire: function(volume) { | ||||
|         Mainloop.timeout_add_seconds(AUTORUN_EXPIRE_TIMEOUT_SECS, function() { | ||||
|             volume.allowAutorun = false; | ||||
|             return false; | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         }); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -64,7 +64,7 @@ function startAppForMount(app, mount) { | ||||
|  | ||||
|     try { | ||||
|         retval = app.launch(files,  | ||||
|                             global.create_app_launch_context()) | ||||
|                             global.create_app_launch_context(0, -1)) | ||||
|     } catch (e) { | ||||
|         log('Unable to launch the application ' + app.get_name() | ||||
|             + ': ' + e.toString()); | ||||
|   | ||||
| @@ -45,7 +45,9 @@ const KeyringDialog = new Lang.Class({ | ||||
|         this.prompt.bind_property('message', subject, 'text', GObject.BindingFlags.SYNC_CREATE); | ||||
|  | ||||
|         this._messageBox.add(subject, | ||||
|                              { y_fill:  false, | ||||
|                              { x_fill: false, | ||||
|                                y_fill:  false, | ||||
|                                x_align: St.Align.START, | ||||
|                                y_align: St.Align.START }); | ||||
|  | ||||
|         let description = new St.Label({ style_class: 'prompt-dialog-description' }); | ||||
| @@ -136,6 +138,7 @@ const KeyringDialog = new Lang.Class({ | ||||
|         warning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         warning.clutter_text.line_wrap = true; | ||||
|         layout.pack(warning, 1, row); | ||||
|         layout.child_set(warning, { x_fill: false, x_align: Clutter.TableAlignment.START }); | ||||
|         this.prompt.bind_property('warning-visible', warning, 'visible', GObject.BindingFlags.SYNC_CREATE); | ||||
|         this.prompt.bind_property('warning', warning, 'text', GObject.BindingFlags.SYNC_CREATE); | ||||
|  | ||||
|   | ||||
| @@ -255,6 +255,7 @@ const NetworkSecretDialog = new Lang.Class({ | ||||
|         case 'leap': | ||||
|         case 'ttls': | ||||
|         case 'peap': | ||||
|         case 'fast': | ||||
|             // TTLS and PEAP are actually much more complicated, but this complication | ||||
|             // is not visible here since we only care about phase2 authentication | ||||
|             // (and don't even care of which one) | ||||
| @@ -308,7 +309,7 @@ const NetworkSecretDialog = new Lang.Class({ | ||||
|             wirelessSetting = this._connection.get_setting_wireless(); | ||||
|             ssid = NetworkManager.utils_ssid_to_utf8(wirelessSetting.get_ssid()); | ||||
|             content.title = _("Authentication required by wireless network"); | ||||
|             content.message = _("Passwords or encryption keys are required to access the wireless network '%s'.").format(ssid); | ||||
|             content.message = _("Passwords or encryption keys are required to access the wireless network “%s”.").format(ssid); | ||||
|             this._getWirelessSecrets(content.secrets, wirelessSetting); | ||||
|             break; | ||||
|         case '802-3-ethernet': | ||||
| @@ -335,7 +336,7 @@ const NetworkSecretDialog = new Lang.Class({ | ||||
|         case 'cdma': | ||||
|         case 'bluetooth': | ||||
|             content.title = _("Mobile broadband network password"); | ||||
|             content.message = _("A password is required to connect to '%s'.").format(connectionSetting.get_id()); | ||||
|             content.message = _("A password is required to connect to “%s”.").format(connectionSetting.get_id()); | ||||
|             this._getMobileSecrets(content.secrets, connectionType); | ||||
|             break; | ||||
|         default: | ||||
| @@ -372,6 +373,12 @@ const VPNRequestHandler = new Lang.Class({ | ||||
|             argv.push('-i'); | ||||
|         if (flags & NMClient.SecretAgentGetSecretsFlags.REQUEST_NEW) | ||||
|             argv.push('-r'); | ||||
|         if (authHelper.supportsHints) { | ||||
|             for (let i = 0; i < hints.length; i++) { | ||||
|                 argv.push('-t'); | ||||
|                 argv.push(hints[i]); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         this._newStylePlugin = authHelper.externalUIMode; | ||||
|  | ||||
| @@ -509,10 +516,12 @@ const VPNRequestHandler = new Lang.Class({ | ||||
|  | ||||
|     _showNewStyleDialog: function() { | ||||
|         let keyfile = new GLib.KeyFile(); | ||||
|         let data; | ||||
|         let contentOverride; | ||||
|  | ||||
|         try { | ||||
|             let data = this._dataStdout.peek_buffer(); | ||||
|             data = this._dataStdout.peek_buffer(); | ||||
|  | ||||
|             keyfile.load_from_data(data.toString(), data.length, | ||||
|                                    GLib.KeyFileFlags.NONE); | ||||
|  | ||||
| @@ -545,13 +554,16 @@ const VPNRequestHandler = new Lang.Class({ | ||||
|                 } | ||||
|             } | ||||
|         } catch(e) { | ||||
|             logError(e, 'error while reading VPN plugin output keyfile'); | ||||
|             // No output is a valid case it means "both secrets are stored" | ||||
|             if (data.length > 0) { | ||||
|                 logError(e, 'error while reading VPN plugin output keyfile'); | ||||
|  | ||||
|             this._agent.respond(this._requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR); | ||||
|             return; | ||||
|                 this._agent.respond(this._requestId, Shell.NetworkAgentResponse.INTERNAL_ERROR); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (contentOverride.secrets.length) { | ||||
|         if (contentOverride && contentOverride.secrets.length) { | ||||
|             // Only show the dialog if we actually have something to ask | ||||
|             this._shellDialog = new NetworkSecretDialog(this._agent, this._requestId, this._connection, 'vpn', [], contentOverride); | ||||
|             this._shellDialog.open(global.get_current_time()); | ||||
| @@ -585,7 +597,15 @@ const NetworkAgent = new Lang.Class({ | ||||
|     Name: 'NetworkAgent', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._native = new Shell.NetworkAgent({ identifier: 'org.gnome.Shell.NetworkAgent' }); | ||||
|         try { | ||||
|             this._native = new Shell.NetworkAgent({ identifier: 'org.gnome.Shell.NetworkAgent', | ||||
|                                                     capabilities: NMClient.SecretAgentCapabilities.VPN_HINTS | ||||
|                                                   }); | ||||
|         } catch(e) { | ||||
|             // Support older versions without NetworkAgent:capabilities | ||||
|             this._native = new Shell.NetworkAgent({ identifier: 'org.gnome.Shell.NetworkAgent' | ||||
|                                                   }); | ||||
|         } | ||||
|  | ||||
|         this._dialogs = { }; | ||||
|         this._vpnRequests = { }; | ||||
| @@ -685,16 +705,23 @@ const NetworkAgent = new Lang.Class({ | ||||
|                     let service = keyfile.get_string('VPN Connection', 'service'); | ||||
|                     let binary = keyfile.get_string('GNOME', 'auth-dialog'); | ||||
|                     let externalUIMode = false; | ||||
|                     let hints = false; | ||||
|  | ||||
|                     try { | ||||
|                         externalUIMode = keyfile.get_boolean('GNOME', 'supports-external-ui-mode'); | ||||
|                     } catch(e) { } // ignore errors if key does not exist | ||||
|  | ||||
|                     try { | ||||
|                         hints = keyfile.get_boolean('GNOME', 'supports-hints'); | ||||
|                     } catch(e) { } // ignore errors if key does not exist | ||||
|  | ||||
|                     let path = binary; | ||||
|                     if (!GLib.path_is_absolute(path)) { | ||||
|                         path = GLib.build_filenamev([Config.LIBEXECDIR, path]); | ||||
|                     } | ||||
|  | ||||
|                     if (GLib.file_test(path, GLib.FileTest.IS_EXECUTABLE)) | ||||
|                         this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode }; | ||||
|                         this._vpnBinaries[service] = { fileName: path, externalUIMode: externalUIMode, supportsHints: hints }; | ||||
|                     else | ||||
|                         throw new Error('VPN plugin at %s is not executable'.format(path)); | ||||
|                 } catch(e) { | ||||
|   | ||||
| @@ -54,7 +54,9 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|                                             text: _("Authentication Required") }); | ||||
|  | ||||
|         messageBox.add(this._subjectLabel, | ||||
|                        { y_fill:  false, | ||||
|                        { x_fill: false, | ||||
|                          y_fill:  false, | ||||
|                          x_align: St.Align.START, | ||||
|                          y_align: St.Align.START }); | ||||
|  | ||||
|         this._descriptionLabel = new St.Label({ style_class: 'prompt-dialog-description', | ||||
| @@ -63,7 +65,9 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|         this._descriptionLabel.clutter_text.line_wrap = true; | ||||
|  | ||||
|         messageBox.add(this._descriptionLabel, | ||||
|                        { y_fill:  true, | ||||
|                        { x_fill: false, | ||||
|                          y_fill:  true, | ||||
|                          x_align: St.Align.START, | ||||
|                          y_align: St.Align.START }); | ||||
|  | ||||
|         if (userNames.length > 1) { | ||||
| @@ -95,7 +99,8 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|         if (userIsRoot) { | ||||
|             let userLabel = new St.Label(({ style_class: 'polkit-dialog-user-root-label', | ||||
|                                             text: userRealName })); | ||||
|             messageBox.add(userLabel); | ||||
|             messageBox.add(userLabel, { x_fill: false, | ||||
|                                         x_align: St.Align.START }); | ||||
|         } else { | ||||
|             let userBox = new St.BoxLayout({ style_class: 'polkit-dialog-user-layout', | ||||
|                                              vertical: false }); | ||||
| @@ -137,7 +142,7 @@ const AuthenticationDialog = new Lang.Class({ | ||||
|         this._errorMessageLabel = new St.Label({ style_class: 'prompt-dialog-error-label' }); | ||||
|         this._errorMessageLabel.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         this._errorMessageLabel.clutter_text.line_wrap = true; | ||||
|         messageBox.add(this._errorMessageLabel); | ||||
|         messageBox.add(this._errorMessageLabel, { x_fill: false, x_align: St.Align.START }); | ||||
|         this._errorMessageLabel.hide(); | ||||
|  | ||||
|         this._infoMessageLabel = new St.Label({ style_class: 'prompt-dialog-info-label' }); | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| @@ -28,6 +29,8 @@ const SCROLLBACK_HISTORY_LINES = 10; | ||||
| // See Notification._onEntryChanged | ||||
| const COMPOSING_STOP_TIMEOUT = 5; | ||||
|  | ||||
| const CLOCK_FORMAT_KEY = 'clock-format'; | ||||
|  | ||||
| const NotificationDirection = { | ||||
|     SENT: 'chat-sent', | ||||
|     RECEIVED: 'chat-received' | ||||
| @@ -621,7 +624,11 @@ const ChatSource = new Lang.Class({ | ||||
|             this.notify(); | ||||
|     }, | ||||
|  | ||||
|     _channelClosed: function() { | ||||
|     destroy: function(reason) { | ||||
|         if (this._destroyed) | ||||
|             return; | ||||
|  | ||||
|         this._destroyed = true; | ||||
|         this._channel.disconnect(this._closedId); | ||||
|         this._channel.disconnect(this._receivedId); | ||||
|         this._channel.disconnect(this._pendingId); | ||||
| @@ -631,7 +638,14 @@ const ChatSource = new Lang.Class({ | ||||
|         this._contact.disconnect(this._notifyAvatarId); | ||||
|         this._contact.disconnect(this._presenceChangedId); | ||||
|  | ||||
|         this.destroy(); | ||||
|         if (this._timestampTimeoutId) | ||||
|             Mainloop.source_remove(this._timestampTimeoutId); | ||||
|  | ||||
|         this.parent(reason); | ||||
|     }, | ||||
|  | ||||
|     _channelClosed: function() { | ||||
|         this.destroy(MessageTray.NotificationDestroyedReason.SOURCE_CLOSED); | ||||
|     }, | ||||
|  | ||||
|     /* All messages are new messages for Telepathy sources */ | ||||
| @@ -675,7 +689,7 @@ const ChatSource = new Lang.Class({ | ||||
|  | ||||
|         this._notifyTimeoutId = 0; | ||||
|  | ||||
|         return false; | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     // This is called for both messages we send from | ||||
| @@ -894,14 +908,14 @@ const ChatNotification = new Lang.Class({ | ||||
|  | ||||
|         let group = props.group; | ||||
|         if (group != this._lastGroup) { | ||||
|             let style = 'chat-group-' + group; | ||||
|             this._lastGroup = group; | ||||
|             this._lastGroupActor = new St.BoxLayout({ style_class: style, | ||||
|                                                       vertical: true }); | ||||
|             this.addActor(this._lastGroupActor); | ||||
|             let emptyLine = new St.Label({ style_class: 'chat-empty-line' }); | ||||
|             this.addActor(emptyLine); | ||||
|         } | ||||
|  | ||||
|         this._lastGroupActor.add(body, props.childProps); | ||||
|         this._lastMessageBox = new St.BoxLayout({ vertical: false }); | ||||
|         this._lastMessageBox.add(body, props.childProps); | ||||
|         this.addActor(this._lastMessageBox); | ||||
|  | ||||
|         this.updated(); | ||||
|  | ||||
| @@ -930,32 +944,81 @@ const ChatNotification = new Lang.Class({ | ||||
|  | ||||
|         let format; | ||||
|  | ||||
|         // Show only the hour if date is on today | ||||
|         if(daysAgo < 1){ | ||||
|             format = "<b>%H:%M</b>"; | ||||
|         } | ||||
|         // Show the word "Yesterday" and time if date is on yesterday | ||||
|         else if(daysAgo <2){ | ||||
|             /* Translators: this is the word "Yesterday" followed by a time string. i.e. "Yesterday, 14:30"*/ | ||||
|             // xgettext:no-c-format | ||||
|             format = _("<b>Yesterday</b>, <b>%H:%M</b>"); | ||||
|         } | ||||
|         // Show a week day and time if date is in the last week | ||||
|         else if (daysAgo < 7) { | ||||
|             /* Translators: this is the week day name followed by a time string. i.e. "Monday, 14:30*/ | ||||
|             // xgettext:no-c-format | ||||
|             format = _("<b>%A</b>, <b>%H:%M</b>"); | ||||
|         let desktopSettings = new Gio.Settings({ schema: 'org.gnome.desktop.interface' }); | ||||
|         let clockFormat = desktopSettings.get_string(CLOCK_FORMAT_KEY); | ||||
|  | ||||
|         } else if (date.getYear() == now.getYear()) { | ||||
|             /* Translators: this is the month name and day number followed by a time string. i.e. "May 25, 14:30"*/ | ||||
|             // xgettext:no-c-format | ||||
|             format = _("<b>%B</b> <b>%d</b>, <b>%H:%M</b>"); | ||||
|         } else { | ||||
|             /* Translators: this is the month name, day number, year number followed by a time string. i.e. "May 25 2012, 14:30"*/ | ||||
|             // xgettext:no-c-format | ||||
|             format = _("<b>%B</b> <b>%d</b> <b>%Y</b>, <b>%H:%M</b> "); | ||||
|         } | ||||
|         switch (clockFormat) { | ||||
|             case '24h': | ||||
|                 // Show only the time if date is on today | ||||
|                 if(daysAgo < 1){ | ||||
|                     /* Translators: Time in 24h format */ | ||||
|                     format = _("%H\u2236%M"); | ||||
|                 } | ||||
|                 // Show the word "Yesterday" and time if date is on yesterday | ||||
|                 else if(daysAgo <2){ | ||||
|                     /* Translators: this is the word "Yesterday" followed by a | ||||
|                      time string in 24h format. i.e. "Yesterday, 14:30" */ | ||||
|                     // xgettext:no-c-format | ||||
|                     format = _("Yesterday, %H\u2236%M"); | ||||
|                 } | ||||
|                 // Show a week day and time if date is in the last week | ||||
|                 else if (daysAgo < 7) { | ||||
|                     /* Translators: this is the week day name followed by a time | ||||
|                      string in 24h format. i.e. "Monday, 14:30" */ | ||||
|                     // xgettext:no-c-format | ||||
|                     format = _("%A, %H\u2236%M"); | ||||
|  | ||||
|                 } else if (date.getYear() == now.getYear()) { | ||||
|                     /* Translators: this is the month name and day number | ||||
|                      followed by a time string in 24h format. | ||||
|                      i.e. "May 25, 14:30" */ | ||||
|                     // xgettext:no-c-format | ||||
|                     format = _("%B %d, %H\u2236%M"); | ||||
|                 } else { | ||||
|                     /* Translators: this is the month name, day number, year | ||||
|                      number followed by a time string in 24h format. | ||||
|                      i.e. "May 25 2012, 14:30" */ | ||||
|                     // xgettext:no-c-format | ||||
|                     format = _("%B %d %Y, %H\u2236%M"); | ||||
|                 } | ||||
|                 break; | ||||
|         default: | ||||
|             /* explicit fall-through */ | ||||
|             case '12h': | ||||
|                 // Show only the time if date is on today | ||||
|                 if(daysAgo < 1){ | ||||
|                     /* Translators: Time in 24h format */ | ||||
|                     format = _("%l\u2236%M %p"); | ||||
|                 } | ||||
|                 // Show the word "Yesterday" and time if date is on yesterday | ||||
|                 else if(daysAgo <2){ | ||||
|                     /* Translators: this is the word "Yesterday" followed by a | ||||
|                      time string in 12h format. i.e. "Yesterday, 2:30 pm" */ | ||||
|                     // xgettext:no-c-format | ||||
|                     format = _("Yesterday, %l\u2236%M %p"); | ||||
|                 } | ||||
|                 // Show a week day and time if date is in the last week | ||||
|                 else if (daysAgo < 7) { | ||||
|                     /* Translators: this is the week day name followed by a time | ||||
|                      string in 12h format. i.e. "Monday, 2:30 pm" */ | ||||
|                     // xgettext:no-c-format | ||||
|                     format = _("%A, %l\u2236%M %p"); | ||||
|  | ||||
|                 } else if (date.getYear() == now.getYear()) { | ||||
|                     /* Translators: this is the month name and day number | ||||
|                      followed by a time string in 12h format. | ||||
|                      i.e. "May 25, 2:30 pm" */ | ||||
|                     // xgettext:no-c-format | ||||
|                     format = _("%B %d, %l\u2236%M %p"); | ||||
|                 } else { | ||||
|                     /* Translators: this is the month name, day number, year | ||||
|                      number followed by a time string in 12h format. | ||||
|                      i.e. "May 25 2012, 2:30 pm"*/ | ||||
|                     // xgettext:no-c-format | ||||
|                     format = _("%B %d %Y, %l\u2236%M %p"); | ||||
|                 } | ||||
|                 break; | ||||
|         } | ||||
|         return date.toLocaleFormat(format); | ||||
|     }, | ||||
|  | ||||
| @@ -965,17 +1028,18 @@ const ChatNotification = new Lang.Class({ | ||||
|         let lastMessageTime = this._history[0].time; | ||||
|         let lastMessageDate = new Date(lastMessageTime * 1000); | ||||
|  | ||||
|         let timeLabel = this._append({ body: this._formatTimestamp(lastMessageDate), | ||||
|                                        group: 'meta', | ||||
|                                        styles: ['chat-meta-message'], | ||||
|                                        childProps: { expand: true, x_fill: false, | ||||
|                                                      x_align: St.Align.END }, | ||||
|                                        noTimestamp: true, | ||||
|                                        timestamp: lastMessageTime }); | ||||
|         let timeLabel = new St.Label({ text: this._formatTimestamp(lastMessageDate), | ||||
|                                        style_class: 'chat-meta-message', | ||||
|                                        x_expand: true, | ||||
|                                        y_expand: true, | ||||
|                                        x_align: Clutter.ActorAlign.END, | ||||
|                                        y_align: Clutter.ActorAlign.END }); | ||||
|  | ||||
|         this._lastMessageBox.add_actor(timeLabel); | ||||
|  | ||||
|         this._filterMessages(); | ||||
|  | ||||
|         return false; | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     appendAliasChange: function(oldAlias, newAlias) { | ||||
| @@ -1013,7 +1077,7 @@ const ChatNotification = new Lang.Class({ | ||||
|  | ||||
|         this.source.setChatState(Tp.ChannelChatState.PAUSED); | ||||
|  | ||||
|         return false; | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     _onEntryChanged: function() { | ||||
| @@ -1206,7 +1270,8 @@ const SubscriptionRequestNotification = new Lang.Class({ | ||||
|  | ||||
|         if (file) { | ||||
|             let uri = file.get_uri(); | ||||
|             iconBox.child = textureCache.load_uri_async(uri, iconBox._size, iconBox._size); | ||||
|             let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|             iconBox.child = textureCache.load_uri_async(uri, iconBox._size, iconBox._size, scaleFactor); | ||||
|         } | ||||
|         else { | ||||
|             iconBox.child = new St.Icon({ icon_name: 'avatar-default', | ||||
| @@ -1339,7 +1404,7 @@ const AccountNotification = new Lang.Class({ | ||||
|             let cmd = 'empathy-accounts --select-account=' + | ||||
|                 account.get_path_suffix(); | ||||
|             let app_info = Gio.app_info_create_from_commandline(cmd, null, 0); | ||||
|             app_info.launch([], global.create_app_launch_context()); | ||||
|             app_info.launch([], global.create_app_launch_context(0, -1)); | ||||
|         })); | ||||
|  | ||||
|         this._enabledId = account.connect('notify::enabled', | ||||
| @@ -1357,7 +1422,12 @@ const AccountNotification = new Lang.Class({ | ||||
|                 if (status == Tp.ConnectionStatus.CONNECTED) { | ||||
|                     this.destroy(); | ||||
|                 } else if (status == Tp.ConnectionStatus.DISCONNECTED) { | ||||
|                     this.update(this.title, this._getMessage(account.connection_error)); | ||||
|                     let connectionError = account.connection_error; | ||||
|  | ||||
|                     if (connectionError == Tp.error_get_dbus_name(Tp.Error.CANCELLED)) | ||||
|                         this.destroy(); | ||||
|                     else | ||||
|                         this.update(this.title, this._getMessage(connectionError)); | ||||
|                 } | ||||
|             })); | ||||
|     }, | ||||
|   | ||||
| @@ -165,6 +165,10 @@ const CtrlAltTabPopup = new Lang.Class({ | ||||
|             this._select(this._previous()); | ||||
|         else if (keysym == Clutter.Right) | ||||
|             this._select(this._next()); | ||||
|         else | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _finish : function(time) { | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Signals = imports.signals; | ||||
| const Lang = imports.lang; | ||||
| const Meta = imports.gi.Meta; | ||||
| @@ -380,6 +381,8 @@ const DashActor = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const baseIconSizes = [ 16, 22, 24, 32, 48, 64 ]; | ||||
|  | ||||
| const Dash = new Lang.Class({ | ||||
|     Name: 'Dash', | ||||
|  | ||||
| @@ -577,7 +580,7 @@ const Dash = new Lang.Class({ | ||||
|                         this._labelShowing = true; | ||||
|                         item.showLabel(); | ||||
|                         this._showLabelTimeoutId = 0; | ||||
|                         return false; | ||||
|                         return GLib.SOURCE_REMOVE; | ||||
|                     })); | ||||
|                 if (this._resetHoverTimeoutId > 0) { | ||||
|                     Mainloop.source_remove(this._resetHoverTimeoutId); | ||||
| @@ -594,7 +597,7 @@ const Dash = new Lang.Class({ | ||||
|                     Lang.bind(this, function() { | ||||
|                         this._labelShowing = false; | ||||
|                         this._resetHoverTimeoutId = 0; | ||||
|                         return false; | ||||
|                         return GLib.SOURCE_REMOVE; | ||||
|                     })); | ||||
|             } | ||||
|         } | ||||
| @@ -631,25 +634,24 @@ const Dash = new Lang.Class({ | ||||
|         let minHeight, natHeight; | ||||
|  | ||||
|         // Enforce the current icon size during the size request | ||||
|         let [currentWidth, currentHeight] = firstIcon.icon.get_size(); | ||||
|  | ||||
|         firstIcon.icon.set_size(this.iconSize, this.iconSize); | ||||
|         firstIcon.setIconSize(this.iconSize); | ||||
|         [minHeight, natHeight] = firstButton.get_preferred_height(-1); | ||||
|  | ||||
|         firstIcon.icon.set_size(currentWidth, currentHeight); | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|         let iconSizes = baseIconSizes.map(function(s) { | ||||
|             return s * scaleFactor; | ||||
|         }); | ||||
|  | ||||
|         // Subtract icon padding and box spacing from the available height | ||||
|         availHeight -= iconChildren.length * (natHeight - this.iconSize) + | ||||
|         availHeight -= iconChildren.length * (natHeight - this.iconSize * scaleFactor) + | ||||
|                        (iconChildren.length - 1) * spacing; | ||||
|  | ||||
|         let availSize = availHeight / iconChildren.length; | ||||
|  | ||||
|         let iconSizes = [ 16, 22, 24, 32, 48, 64 ]; | ||||
|  | ||||
|         let newIconSize = 16; | ||||
|         let newIconSize = baseIconSizes[0]; | ||||
|         for (let i = 0; i < iconSizes.length; i++) { | ||||
|             if (iconSizes[i] < availSize) | ||||
|                 newIconSize = iconSizes[i]; | ||||
|                 newIconSize = baseIconSizes[i]; | ||||
|         } | ||||
|  | ||||
|         if (newIconSize == this.iconSize) | ||||
|   | ||||
| @@ -63,9 +63,14 @@ const DateMenuButton = new Lang.Class({ | ||||
|         hbox.add(vbox); | ||||
|  | ||||
|         // Date | ||||
|         this._date = new St.Label({ style_class: 'datemenu-date-label', | ||||
|                                     can_focus: true }); | ||||
|         vbox.add(this._date); | ||||
|         this._date = new St.Button({ style_class: 'datemenu-date-label', | ||||
|                                      can_focus: true, | ||||
|                                    }); | ||||
|         this._date.connect('clicked', | ||||
|                            Lang.bind(this, function() { | ||||
|                                this._calendar.setDate(new Date(), false); | ||||
|                            })); | ||||
|         vbox.add(this._date, { x_fill: false  }); | ||||
|  | ||||
|         this._eventList = new Calendar.EventsList(); | ||||
|         this._calendar = new Calendar.Calendar(); | ||||
| @@ -113,22 +118,7 @@ const DateMenuButton = new Lang.Class({ | ||||
|         this.menu.connect('open-state-changed', Lang.bind(this, function(menu, isOpen) { | ||||
|             if (isOpen) { | ||||
|                 let now = new Date(); | ||||
|                 /* Passing true to setDate() forces events to be reloaded. We | ||||
|                  * want this behavior, because | ||||
|                  * | ||||
|                  *   o It will cause activation of the calendar server which is | ||||
|                  *     useful if it has crashed | ||||
|                  * | ||||
|                  *   o It will cause the calendar server to reload events which | ||||
|                  *     is useful if dynamic updates are not supported or not | ||||
|                  *     properly working | ||||
|                  * | ||||
|                  * Since this only happens when the menu is opened, the cost | ||||
|                  * isn't very big. | ||||
|                  */ | ||||
|                 this._calendar.setDate(now, true); | ||||
|                 // No need to update this._eventList as ::selected-date-changed | ||||
|                 // signal will fire | ||||
|                 this._calendar.setDate(now); | ||||
|             } | ||||
|         })); | ||||
|  | ||||
| @@ -201,7 +191,7 @@ const DateMenuButton = new Lang.Class({ | ||||
|          */ | ||||
|         let dateFormat = _("%A %B %e, %Y"); | ||||
|         let displayDate = new Date(); | ||||
|         this._date.set_text(displayDate.toLocaleFormat(dateFormat)); | ||||
|         this._date.set_label(displayDate.toLocaleFormat(dateFormat)); | ||||
|     }, | ||||
|  | ||||
|     _getCalendarApp: function() { | ||||
| @@ -217,7 +207,7 @@ const DateMenuButton = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _getClockApp: function() { | ||||
|         return Shell.AppSystem.get_default().lookup_app('gnome-clocks.desktop'); | ||||
|         return Shell.AppSystem.get_default().lookup_app('org.gnome.clocks.desktop'); | ||||
|     }, | ||||
|  | ||||
|     _onOpenCalendarActivate: function() { | ||||
| @@ -226,7 +216,7 @@ const DateMenuButton = new Lang.Class({ | ||||
|         let app = this._getCalendarApp(); | ||||
|         if (app.get_id() == 'evolution.desktop') | ||||
|             app = Gio.DesktopAppInfo.new('evolution-calendar.desktop'); | ||||
|         app.launch([], global.create_app_launch_context()); | ||||
|         app.launch([], global.create_app_launch_context(0, -1)); | ||||
|     }, | ||||
|  | ||||
|     _onOpenClocksActivate: function() { | ||||
|   | ||||
							
								
								
									
										20
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							
							
						
						
									
										20
									
								
								js/ui/dnd.js
									
									
									
									
									
								
							| @@ -106,10 +106,10 @@ const _Draggable = new Lang.Class({ | ||||
|  | ||||
|     _onButtonPress : function (actor, event) { | ||||
|         if (event.get_button() != 1) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (Tweener.getTweenCount(actor)) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         this._buttonDown = true; | ||||
|         this._grabActor(); | ||||
| @@ -118,7 +118,7 @@ const _Draggable = new Lang.Class({ | ||||
|         this._dragStartX = stageX; | ||||
|         this._dragStartY = stageY; | ||||
|  | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _grabActor: function() { | ||||
| @@ -164,11 +164,11 @@ const _Draggable = new Lang.Class({ | ||||
|             } else if (this._dragActor != null && !this._animationInProgress) { | ||||
|                 // Drag must have been cancelled with Esc. | ||||
|                 this._dragComplete(); | ||||
|                 return true; | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } else { | ||||
|                 // Drag has never started. | ||||
|                 this._ungrabActor(); | ||||
|                 return false; | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|             } | ||||
|         // We intercept MOTION event to figure out if the drag has started and to draw | ||||
|         // this._dragActor under the pointer when dragging is in progress | ||||
| @@ -184,11 +184,11 @@ const _Draggable = new Lang.Class({ | ||||
|             let symbol = event.get_key_symbol(); | ||||
|             if (symbol == Clutter.Escape) { | ||||
|                 this._cancelDrag(event.get_time()); | ||||
|                 return true; | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
| @@ -362,7 +362,7 @@ const _Draggable = new Lang.Class({ | ||||
|                 let result = motionFunc(dragEvent); | ||||
|                 if (result != DragMotionResult.CONTINUE) { | ||||
|                     global.screen.set_cursor(DRAG_CURSOR_MAP[result]); | ||||
|                     return false; | ||||
|                     return GLib.SOURCE_REMOVE; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
| @@ -380,13 +380,13 @@ const _Draggable = new Lang.Class({ | ||||
|                                                              0); | ||||
|                 if (result != DragMotionResult.CONTINUE) { | ||||
|                     global.screen.set_cursor(DRAG_CURSOR_MAP[result]); | ||||
|                     return false; | ||||
|                     return GLib.SOURCE_REMOVE; | ||||
|                 } | ||||
|             } | ||||
|             target = target.get_parent(); | ||||
|         } | ||||
|         global.screen.set_cursor(Meta.Cursor.DND_IN_DRAG); | ||||
|         return false; | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     _queueUpdateDragHover: function() { | ||||
|   | ||||
| @@ -13,9 +13,7 @@ | ||||
|  * GNU General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program; if not, write to the Free Software | ||||
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA | ||||
|  * 02111-1307, USA. | ||||
|  * along with this program; if not, see <http://www.gnu.org/licenses/>. | ||||
|  */ | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| @@ -27,9 +25,11 @@ const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Pango = imports.gi.Pango; | ||||
| const Polkit = imports.gi.Polkit; | ||||
| const St = imports.gi.St; | ||||
| const Shell = imports.gi.Shell; | ||||
|  | ||||
| const CheckBox = imports.ui.checkBox; | ||||
| const GnomeSession = imports.misc.gnomeSession; | ||||
| const LoginManager = imports.misc.loginManager; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| @@ -38,8 +38,10 @@ const UserWidget = imports.ui.userWidget; | ||||
|  | ||||
| let _endSessionDialog = null; | ||||
|  | ||||
| const TRIGGER_OFFLINE_UPDATE = '/usr/libexec/pk-trigger-offline-update'; | ||||
|  | ||||
| const _ITEM_ICON_SIZE = 48; | ||||
| const _DIALOG_ICON_SIZE = 32; | ||||
| const _DIALOG_ICON_SIZE = 48; | ||||
|  | ||||
| const GSM_SESSION_MANAGER_LOGOUT_FORCE = 2; | ||||
|  | ||||
| @@ -73,6 +75,7 @@ const logoutDialogContent = { | ||||
|                         "You will be logged out automatically in %d seconds.", | ||||
|                         seconds).format(seconds); | ||||
|     }, | ||||
|     showBatteryWarning: false, | ||||
|     confirmButtons: [{ signal: 'ConfirmedLogout', | ||||
|                        label:  C_("button", "Log Out") }], | ||||
|     iconStyleClass: 'end-session-dialog-logout-icon', | ||||
| @@ -81,11 +84,14 @@ const logoutDialogContent = { | ||||
|  | ||||
| const shutdownDialogContent = { | ||||
|     subject: C_("title", "Power Off"), | ||||
|     subjectWithUpdates: C_("title", "Install Updates & Power Off"), | ||||
|     description: function(seconds) { | ||||
|         return ngettext("The system will power off automatically in %d second.", | ||||
|                         "The system will power off automatically in %d seconds.", | ||||
|                         seconds).format(seconds); | ||||
|     }, | ||||
|     checkBoxText: C_("checkbox", "Install pending software updates"), | ||||
|     showBatteryWarning: true, | ||||
|     confirmButtons: [{ signal: 'ConfirmedReboot', | ||||
|                        label:  C_("button", "Restart") }, | ||||
|                      { signal: 'ConfirmedShutdown', | ||||
| @@ -102,6 +108,7 @@ const restartDialogContent = { | ||||
|                         "The system will restart automatically in %d seconds.", | ||||
|                         seconds).format(seconds); | ||||
|     }, | ||||
|     showBatteryWarning: false, | ||||
|     confirmButtons: [{ signal: 'ConfirmedReboot', | ||||
|                        label:  C_("button", "Restart") }], | ||||
|     iconName: 'view-refresh-symbolic', | ||||
| @@ -117,8 +124,11 @@ const restartInstallDialogContent = { | ||||
|                         "The system will automatically restart and install updates in %d seconds.", | ||||
|                         seconds).format(seconds); | ||||
|     }, | ||||
|     showBatteryWarning: true, | ||||
|     confirmButtons: [{ signal: 'ConfirmedReboot', | ||||
|                        label:  C_("button", "Restart & Install") }], | ||||
|                        label:  C_("button", "Restart & Install") }], | ||||
|     unusedFutureButtonForTranslation: C_("button", "Install & Power Off"), | ||||
|     unusedFutureCheckBoxForTranslation: C_("checkbox", "Power off after updates are installed"), | ||||
|     iconName: 'view-refresh-symbolic', | ||||
|     iconStyleClass: 'end-session-dialog-shutdown-icon', | ||||
|     showOtherSessions: true, | ||||
| @@ -145,6 +155,14 @@ const LogindSessionIface = '<node> \ | ||||
|  | ||||
| const LogindSession = Gio.DBusProxy.makeProxyWrapper(LogindSessionIface); | ||||
|  | ||||
| const UPowerIface = '<node> \ | ||||
| <interface name="org.freedesktop.UPower"> \ | ||||
|     <property name="OnBattery" type="b" access="read"/> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const UPowerProxy = Gio.DBusProxy.makeProxyWrapper(UPowerIface); | ||||
|  | ||||
| function findAppFromInhibitor(inhibitor) { | ||||
|     let desktopFile; | ||||
|     try { | ||||
| @@ -197,6 +215,18 @@ function _setLabelText(label, text) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| function _setCheckBoxLabel(checkBox, text) { | ||||
|     let label = checkBox.getLabelActor(); | ||||
|  | ||||
|     if (text) { | ||||
|         label.set_text(text); | ||||
|         checkBox.actor.show(); | ||||
|     } else { | ||||
|         label.set_text(''); | ||||
|         checkBox.actor.hide(); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function init() { | ||||
|     // This always returns the same singleton object | ||||
|     // By instantiating it initially, we register the | ||||
| @@ -216,6 +246,20 @@ const EndSessionDialog = new Lang.Class({ | ||||
|         this._userManager = AccountsService.UserManager.get_default(); | ||||
|         this._user = this._userManager.get_user(GLib.get_user_name()); | ||||
|         this._updatesFile = Gio.File.new_for_path('/system-update'); | ||||
|         this._preparedUpdateFile = Gio.File.new_for_path('/var/lib/PackageKit/prepared-update'); | ||||
|  | ||||
|         this._powerProxy = new UPowerProxy(Gio.DBus.system, | ||||
|                                            'org.freedesktop.UPower', | ||||
|                                            '/org/freedesktop/UPower', | ||||
|                                            Lang.bind(this, function(proxy, error) { | ||||
|                                                if (error) { | ||||
|                                                    log(error.message); | ||||
|                                                    return; | ||||
|                                                } | ||||
|                                                this._powerProxy.connect('g-properties-changed', | ||||
|                                                                         Lang.bind(this, this._sync)); | ||||
|                                                this._sync(); | ||||
|                                            })); | ||||
|  | ||||
|         this._secondsLeft = 0; | ||||
|         this._totalSecondsToStayOpen = 0; | ||||
| @@ -242,14 +286,17 @@ const EndSessionDialog = new Lang.Class({ | ||||
|                                 x_align: St.Align.END, | ||||
|                                 y_align: St.Align.START }); | ||||
|  | ||||
|         let messageLayout = new St.BoxLayout({ vertical: true }); | ||||
|         let messageLayout = new St.BoxLayout({ vertical: true, | ||||
|                                                style_class: 'end-session-dialog-layout' }); | ||||
|         mainContentLayout.add(messageLayout, | ||||
|                               { y_align: St.Align.START }); | ||||
|  | ||||
|         this._subjectLabel = new St.Label({ style_class: 'end-session-dialog-subject' }); | ||||
|  | ||||
|         messageLayout.add(this._subjectLabel, | ||||
|                           { y_fill:  false, | ||||
|                           { x_fill: false, | ||||
|                             y_fill:  false, | ||||
|                             x_align: St.Align.START, | ||||
|                             y_align: St.Align.START }); | ||||
|  | ||||
|         this._descriptionLabel = new St.Label({ style_class: 'end-session-dialog-description' }); | ||||
| @@ -260,6 +307,16 @@ const EndSessionDialog = new Lang.Class({ | ||||
|                           { y_fill:  true, | ||||
|                             y_align: St.Align.START }); | ||||
|  | ||||
|         this._checkBox = new CheckBox.CheckBox(); | ||||
|         this._checkBox.actor.connect('clicked', Lang.bind(this, this._sync)); | ||||
|         messageLayout.add(this._checkBox.actor); | ||||
|  | ||||
|         this._batteryWarning = new St.Label({ style_class: 'end-session-dialog-warning', | ||||
|                                               text: _("Running on battery power: please plug in before installing updates.") }); | ||||
|         this._batteryWarning.clutter_text.ellipsize = Pango.EllipsizeMode.NONE; | ||||
|         this._batteryWarning.clutter_text.line_wrap = true; | ||||
|         messageLayout.add(this._batteryWarning); | ||||
|  | ||||
|         this._scrollView = new St.ScrollView({ style_class: 'end-session-dialog-list' }); | ||||
|         this._scrollView.set_policy(Gtk.PolicyType.NEVER, Gtk.PolicyType.AUTOMATIC); | ||||
|         this.contentLayout.add(this._scrollView, | ||||
| @@ -285,6 +342,12 @@ const EndSessionDialog = new Lang.Class({ | ||||
|         this._inhibitorSection.add_actor(this._sessionHeader); | ||||
|         this._inhibitorSection.add_actor(this._sessionList); | ||||
|  | ||||
|         try { | ||||
|             this._updatesPermission = Polkit.Permission.new_sync("org.freedesktop.packagekit.trigger-offline-update", null, null); | ||||
|         } catch(e) { | ||||
|             log('No permission to trigger offline updates: %s'.format(e.toString())); | ||||
|         } | ||||
|  | ||||
|         this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(EndSessionDialogIface, this); | ||||
|         this._dbusImpl.export(Gio.DBus.session, '/org/gnome/SessionManager/EndSessionDialog'); | ||||
|     }, | ||||
| @@ -299,13 +362,22 @@ const EndSessionDialog = new Lang.Class({ | ||||
|         if (!open) | ||||
|             return; | ||||
|  | ||||
|         if (this._type == 2 && this._updatesFile.query_exists(null)) | ||||
|             this._type = 3; | ||||
|  | ||||
|         let dialogContent = DialogContent[this._type]; | ||||
|  | ||||
|         let subject = dialogContent.subject; | ||||
|  | ||||
|         // Use different title when we are installing updates | ||||
|         if (dialogContent.subjectWithUpdates && this._checkBox.actor.checked) | ||||
|             subject = dialogContent.subjectWithUpdates; | ||||
|  | ||||
|         if (dialogContent.showBatteryWarning) { | ||||
|             // Warn when running on battery power | ||||
|             if (this._powerProxy.OnBattery && this._checkBox.actor.checked) | ||||
|                 this._batteryWarning.opacity = 255; | ||||
|             else | ||||
|                 this._batteryWarning.opacity = 0; | ||||
|         } | ||||
|  | ||||
|         let description; | ||||
|         let displayTime = _roundSecondsToInterval(this._totalSecondsToStayOpen, | ||||
|                                                   this._secondsLeft, | ||||
| @@ -388,15 +460,75 @@ const EndSessionDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _confirm: function(signal) { | ||||
|         this._fadeOutDialog(); | ||||
|         this._stopTimer(); | ||||
|         this._dbusImpl.emit_signal(signal, null); | ||||
|         let callback = Lang.bind(this, function() { | ||||
|             this._fadeOutDialog(); | ||||
|             this._stopTimer(); | ||||
|             this._dbusImpl.emit_signal(signal, null); | ||||
|         }); | ||||
|  | ||||
|         // Offline update not available; just emit the signal | ||||
|         if (!this._checkBox.actor.visible) { | ||||
|             callback(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         // Trigger the offline update as requested | ||||
|         if (this._checkBox.actor.checked) { | ||||
|             switch (signal) { | ||||
|                 case "ConfirmedReboot": | ||||
|                     this._triggerOfflineUpdateReboot(callback); | ||||
|                     break; | ||||
|                 case "ConfirmedShutdown": | ||||
|                     // To actually trigger the offline update, we need to | ||||
|                     // reboot to do the upgrade. When the upgrade is complete, | ||||
|                     // the computer will shut down automatically. | ||||
|                     signal = "ConfirmedReboot"; | ||||
|                     this._triggerOfflineUpdateShutdown(callback); | ||||
|                     break; | ||||
|                 default: | ||||
|                     callback(); | ||||
|                     break; | ||||
|             } | ||||
|         } else { | ||||
|             this._triggerOfflineUpdateCancel(callback); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onOpened: function() { | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     _triggerOfflineUpdateReboot: function(callback) { | ||||
|         this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, 'reboot'], callback); | ||||
|     }, | ||||
|  | ||||
|     _triggerOfflineUpdateShutdown: function(callback) { | ||||
|         this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, 'power-off'], callback); | ||||
|     }, | ||||
|  | ||||
|     _triggerOfflineUpdateCancel: function(callback) { | ||||
|         this._pkexecSpawn([TRIGGER_OFFLINE_UPDATE, '--cancel'], callback); | ||||
|     }, | ||||
|  | ||||
|     _pkexecSpawn: function(argv, callback) { | ||||
|         let ret, pid; | ||||
|         try { | ||||
|             [ret, pid] = GLib.spawn_async(null, ['pkexec'].concat(argv), null, | ||||
|                                           GLib.SpawnFlags.DO_NOT_REAP_CHILD | GLib.SpawnFlags.SEARCH_PATH, | ||||
|                                           null); | ||||
|         } catch (e) { | ||||
|             log('Error spawning "pkexec %s": %s'.format(argv.join(' '), e.toString())); | ||||
|             callback(); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         GLib.child_watch_add(GLib.PRIORITY_DEFAULT, pid, function(pid, status) { | ||||
|             GLib.spawn_close_pid(pid); | ||||
|  | ||||
|             callback(); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     _startTimer: function() { | ||||
|         let startTime = GLib.get_monotonic_time(); | ||||
|         this._secondsLeft = this._totalSecondsToStayOpen; | ||||
| @@ -409,7 +541,7 @@ const EndSessionDialog = new Lang.Class({ | ||||
|                 this._secondsLeft = this._totalSecondsToStayOpen - secondsElapsed; | ||||
|                 if (this._secondsLeft > 0) { | ||||
|                     this._sync(); | ||||
|                     return true; | ||||
|                     return GLib.SOURCE_CONTINUE; | ||||
|                 } | ||||
|  | ||||
|                 let dialogContent = DialogContent[this._type]; | ||||
| @@ -417,7 +549,7 @@ const EndSessionDialog = new Lang.Class({ | ||||
|                 this._confirm(button.signal); | ||||
|                 this._timerId = 0; | ||||
|  | ||||
|                 return false; | ||||
|                 return GLib.SOURCE_REMOVE; | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
| @@ -544,6 +676,9 @@ const EndSessionDialog = new Lang.Class({ | ||||
|         this._totalSecondsToStayOpen = totalSecondsToStayOpen; | ||||
|         this._type = type; | ||||
|  | ||||
|         if (this._type == 2 && this._updatesFile.query_exists(null)) | ||||
|             this._type = 3; | ||||
|  | ||||
|         this._applications = []; | ||||
|         this._applicationList.destroy_all_children(); | ||||
|  | ||||
| @@ -556,6 +691,8 @@ const EndSessionDialog = new Lang.Class({ | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let dialogContent = DialogContent[this._type]; | ||||
|  | ||||
|         for (let i = 0; i < inhibitorObjectPaths.length; i++) { | ||||
|             let inhibitor = new GnomeSession.Inhibitor(inhibitorObjectPaths[i], Lang.bind(this, function(proxy, error) { | ||||
|                 this._onInhibitorLoaded(proxy); | ||||
| @@ -564,9 +701,23 @@ const EndSessionDialog = new Lang.Class({ | ||||
|             this._applications.push(inhibitor); | ||||
|         } | ||||
|  | ||||
|         if (DialogContent[type].showOtherSessions) | ||||
|         if (dialogContent.showOtherSessions) | ||||
|             this._loadSessions(); | ||||
|  | ||||
|         let preparedUpdate = this._preparedUpdateFile.query_exists(null); | ||||
|         let updateAlreadyTriggered = this._updatesFile.query_exists(null); | ||||
|         let updatesAllowed = this._updatesPermission && this._updatesPermission.allowed; | ||||
|  | ||||
|         _setCheckBoxLabel(this._checkBox, dialogContent.checkBoxText); | ||||
|         this._checkBox.actor.visible = (dialogContent.checkBoxText && preparedUpdate && updatesAllowed); | ||||
|         this._checkBox.actor.checked = (preparedUpdate && updateAlreadyTriggered); | ||||
|  | ||||
|         // We show the warning either together with the checkbox, or when | ||||
|         // updates have already been triggered, but the user doesn't have | ||||
|         // enough permissions to cancel them. | ||||
|         this._batteryWarning.visible = (dialogContent.showBatteryWarning && | ||||
|                                         (this._checkBox.actor.visible || preparedUpdate && updateAlreadyTriggered && !updatesAllowed)); | ||||
|  | ||||
|         this._updateButtons(); | ||||
|  | ||||
|         if (!this.open(timestamp)) { | ||||
|   | ||||
| @@ -5,6 +5,8 @@ imports.gi.versions.Gio = '2.0'; | ||||
| imports.gi.versions.Gdk = '3.0'; | ||||
| imports.gi.versions.GdkPixbuf = '2.0'; | ||||
| imports.gi.versions.Gtk = '3.0'; | ||||
| imports.gi.versions.TelepathyGLib = '0.12'; | ||||
| imports.gi.versions.TelepathyLogger = '0.2'; | ||||
|  | ||||
| const Clutter = imports.gi.Clutter;; | ||||
| const Gettext = imports.gettext; | ||||
|   | ||||
| @@ -201,7 +201,7 @@ const InstallExtensionDialog = new Lang.Class({ | ||||
|                            default: true | ||||
|                          }]); | ||||
|  | ||||
|         let message = _("Download and install '%s' from extensions.gnome.org?").format(info.name); | ||||
|         let message = _("Download and install “%s” from extensions.gnome.org?").format(info.name); | ||||
|  | ||||
|         let box = new St.BoxLayout(); | ||||
|         this.contentLayout.add(box); | ||||
|   | ||||
| @@ -38,6 +38,7 @@ const connect = Lang.bind(_signals, _signals.connect); | ||||
| const disconnect = Lang.bind(_signals, _signals.disconnect); | ||||
|  | ||||
| const ENABLED_EXTENSIONS_KEY = 'enabled-extensions'; | ||||
| const EXTENSION_DISABLE_VERSION_CHECK_KEY = 'disable-extension-version-validation'; | ||||
|  | ||||
| var initted = false; | ||||
| var enabled; | ||||
| @@ -156,7 +157,9 @@ function loadExtension(extension) { | ||||
|     // Default to error, we set success as the last step | ||||
|     extension.state = ExtensionState.ERROR; | ||||
|  | ||||
|     if (ExtensionUtils.isOutOfDate(extension)) { | ||||
|     let checkVersion = !global.settings.get_boolean(EXTENSION_DISABLE_VERSION_CHECK_KEY); | ||||
|  | ||||
|     if (checkVersion && ExtensionUtils.isOutOfDate(extension)) { | ||||
|         extension.state = ExtensionState.OUT_OF_DATE; | ||||
|     } else { | ||||
|         let enabled = enabledExtensions.indexOf(extension.uuid) != -1; | ||||
| @@ -267,8 +270,19 @@ function onEnabledExtensionsChanged() { | ||||
|     enabledExtensions = newEnabledExtensions; | ||||
| } | ||||
|  | ||||
| function _onVersionValidationChanged() { | ||||
|     if (Main.sessionMode.allowExtensions) { | ||||
|         enabledExtensions.forEach(function(uuid) { | ||||
|             if (ExtensionUtils.extensions[uuid]) | ||||
|                 reloadExtension(ExtensionUtils.extensions[uuid]); | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|  | ||||
| function _loadExtensions() { | ||||
|     global.settings.connect('changed::' + ENABLED_EXTENSIONS_KEY, onEnabledExtensionsChanged); | ||||
|     global.settings.connect('changed::' + EXTENSION_DISABLE_VERSION_CHECK_KEY, _onVersionValidationChanged); | ||||
|  | ||||
|     enabledExtensions = getEnabledExtensions(); | ||||
|  | ||||
|     let finder = new ExtensionUtils.ExtensionFinder(); | ||||
|   | ||||
| @@ -32,9 +32,11 @@ const FocusCaretTracker = new Lang.Class({ | ||||
|     Name: 'FocusCaretTracker', | ||||
|  | ||||
|     _init: function() { | ||||
|         Atspi.init(); | ||||
|         Atspi.set_timeout(250, 250); | ||||
|         this._atspiListener = Atspi.EventListener.new(Lang.bind(this, this._onChanged)); | ||||
|  | ||||
|         this._atspiInited = false; | ||||
|         this._focusListenerRegistered = false; | ||||
|         this._caretListenerRegistered = false; | ||||
|     }, | ||||
|  | ||||
|     _onChanged: function(event) { | ||||
| @@ -44,22 +46,50 @@ const FocusCaretTracker = new Lang.Class({ | ||||
|             this.emit('caret-moved', event); | ||||
|     }, | ||||
|  | ||||
|     _initAtspi: function() { | ||||
|         if (!this._atspiInited) { | ||||
|             Atspi.init(); | ||||
|             Atspi.set_timeout(250, 250); | ||||
|             this._atspiInited = true; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     registerFocusListener: function() { | ||||
|         return this._atspiListener.register(STATECHANGED + ':focused') && | ||||
|                this._atspiListener.register(STATECHANGED + ':selected'); | ||||
|         if (this._focusListenerRegistered) | ||||
|             return; | ||||
|  | ||||
|         this._initAtspi(); | ||||
|  | ||||
|         this._atspiListener.register(STATECHANGED + ':focused'); | ||||
|         this._atspiListener.register(STATECHANGED + ':selected'); | ||||
|         this._focusListenerRegistered = true; | ||||
|     }, | ||||
|  | ||||
|     registerCaretListener: function() { | ||||
|         return this._atspiListener.register(CARETMOVED); | ||||
|         if (this._caretListenerRegistered) | ||||
|             return; | ||||
|  | ||||
|         this._initAtspi(); | ||||
|  | ||||
|         this._atspiListener.register(CARETMOVED); | ||||
|         this._caretListenerRegistered = true; | ||||
|     }, | ||||
|  | ||||
|     deregisterFocusListener: function() { | ||||
|         return this._atspiListener.deregister(STATECHANGED + ':focused') && | ||||
|                this._atspiListener.deregister(STATECHANGED + ':selected'); | ||||
|         if (!this._focusListenerRegistered) | ||||
|             return; | ||||
|  | ||||
|         this._atspiListener.deregister(STATECHANGED + ':focused'); | ||||
|         this._atspiListener.deregister(STATECHANGED + ':selected'); | ||||
|         this._focusListenerRegistered = false; | ||||
|     }, | ||||
|  | ||||
|     deregisterCaretListener: function() { | ||||
|         return this._atspiListener.deregister(CARETMOVED); | ||||
|         if (!this._caretListenerRegistered) | ||||
|             return; | ||||
|  | ||||
|         this._atspiListener.deregister(CARETMOVED); | ||||
|         this._caretListenerRegistered = false; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(FocusCaretTracker.prototype); | ||||
|   | ||||
| @@ -280,7 +280,7 @@ const GrabHelper = new Lang.Class({ | ||||
|         if (type == Clutter.EventType.KEY_PRESS && | ||||
|             event.get_key_symbol() == Clutter.KEY_Escape) { | ||||
|             this.ungrab({ isUser: true }); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|  | ||||
|         let press = type == Clutter.EventType.BUTTON_PRESS; | ||||
| @@ -289,14 +289,14 @@ const GrabHelper = new Lang.Class({ | ||||
|  | ||||
|         if (release && this._ignoreRelease) { | ||||
|             this._ignoreRelease = false; | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|  | ||||
|         if (this._isWithinGrabbedActor(event.get_source())) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (Main.keyboard.shouldTakeEvent(event)) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (button) { | ||||
|             // If we have a press event, ignore the next event, | ||||
| @@ -305,9 +305,9 @@ const GrabHelper = new Lang.Class({ | ||||
|                 this._ignoreRelease = true; | ||||
|             let i = this._actorInGrabStack(event.get_source()) + 1; | ||||
|             this.ungrab({ actor: this._grabStack[i].actor, isUser: true }); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
| }); | ||||
|   | ||||
| @@ -32,6 +32,7 @@ const CandidateArea = new Lang.Class({ | ||||
|             let j = i; | ||||
|             box.connect('button-release-event', Lang.bind(this, function(actor, event) { | ||||
|                 this.emit('candidate-clicked', j, event.get_button(), event.get_state()); | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|             })); | ||||
|         } | ||||
|  | ||||
| @@ -114,9 +115,6 @@ const CandidatePopup = new Lang.Class({ | ||||
|     Name: 'CandidatePopup', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._cursor = new St.Bin({ opacity: 0 }); | ||||
|         Main.uiGroup.add_actor(this._cursor); | ||||
|  | ||||
|         this._boxPointer = new BoxPointer.BoxPointer(St.Side.TOP); | ||||
|         this._boxPointer.actor.visible = false; | ||||
|         this._boxPointer.actor.style_class = 'candidate-popup-boxpointer'; | ||||
| @@ -157,10 +155,9 @@ const CandidatePopup = new Lang.Class({ | ||||
|  | ||||
|         panelService.connect('set-cursor-location', | ||||
|                              Lang.bind(this, function(ps, x, y, w, h) { | ||||
|                                  this._cursor.set_position(x, y); | ||||
|                                  this._cursor.set_size(w, h); | ||||
|                                  Main.layoutManager.setDummyCursorGeometry(x, y, w, h); | ||||
|                                  if (this._boxPointer.actor.visible) | ||||
|                                      this._boxPointer.setPosition(this._cursor, 0); | ||||
|                                      this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0); | ||||
|                              })); | ||||
|         panelService.connect('update-preedit-text', | ||||
|                              Lang.bind(this, function(ps, text, cursorPosition, visible) { | ||||
| @@ -252,7 +249,7 @@ const CandidatePopup = new Lang.Class({ | ||||
|                          this._candidateArea.actor.visible); | ||||
|  | ||||
|         if (isVisible) { | ||||
|             this._boxPointer.setPosition(this._cursor, 0); | ||||
|             this._boxPointer.setPosition(Main.layoutManager.dummyCursor, 0); | ||||
|             this._boxPointer.show(BoxPointer.PopupAnimation.NONE); | ||||
|             this._boxPointer.actor.raise_top(); | ||||
|         } else { | ||||
|   | ||||
| @@ -143,11 +143,6 @@ const BaseIcon = new Lang.Class({ | ||||
|         this.icon = this.createIcon(this.iconSize); | ||||
|  | ||||
|         this._iconBin.child = this.icon; | ||||
|  | ||||
|         // The icon returned by createIcon() might actually be smaller than | ||||
|         // the requested icon size (for instance StTextureCache does this | ||||
|         // for fallback icons), so set the size explicitly. | ||||
|         this._iconBin.set_size(this.iconSize, this.iconSize); | ||||
|     }, | ||||
|  | ||||
|     _onStyleChanged: function() { | ||||
| @@ -219,6 +214,20 @@ const IconGrid = new Lang.Class({ | ||||
|         this._grid.connect('get-preferred-width', Lang.bind(this, this._getPreferredWidth)); | ||||
|         this._grid.connect('get-preferred-height', Lang.bind(this, this._getPreferredHeight)); | ||||
|         this._grid.connect('allocate', Lang.bind(this, this._allocate)); | ||||
|         this._grid.connect('actor-added', Lang.bind(this, this._childAdded)); | ||||
|         this._grid.connect('actor-removed', Lang.bind(this, this._childRemoved)); | ||||
|     }, | ||||
|  | ||||
|     _keyFocusIn: function(actor) { | ||||
|         this.emit('key-focus-in', actor); | ||||
|     }, | ||||
|  | ||||
|     _childAdded: function(grid, child) { | ||||
|         child._iconGridKeyFocusInId = child.connect('key-focus-in', Lang.bind(this, this._keyFocusIn)); | ||||
|     }, | ||||
|  | ||||
|     _childRemoved: function(grid, child) { | ||||
|         child.disconnect(child._iconGridKeyFocusInId); | ||||
|     }, | ||||
|  | ||||
|     _getPreferredWidth: function (grid, forHeight, alloc) { | ||||
| @@ -433,6 +442,10 @@ const IconGrid = new Lang.Class({ | ||||
|             this._grid.add_actor(item.actor); | ||||
|     }, | ||||
|  | ||||
|     removeItem: function(item) { | ||||
|         this._grid.remove_child(item.actor); | ||||
|     }, | ||||
|  | ||||
|     getItemAtIndex: function(index) { | ||||
|         return this._grid.get_child_at_index(index); | ||||
|     }, | ||||
| @@ -528,6 +541,7 @@ const IconGrid = new Lang.Class({ | ||||
|         } | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(IconGrid.prototype); | ||||
|  | ||||
| const PaginatedIconGrid = new Lang.Class({ | ||||
|     Name: 'PaginatedIconGrid', | ||||
|   | ||||
| @@ -82,8 +82,16 @@ const Key = new Lang.Class({ | ||||
|                                       style_class: 'keyboard-key' }); | ||||
|  | ||||
|         button.key_width = this._key.width; | ||||
|         button.connect('button-press-event', Lang.bind(this, function () { this._key.press(); })); | ||||
|         button.connect('button-release-event', Lang.bind(this, function () { this._key.release(); })); | ||||
|         button.connect('button-press-event', Lang.bind(this, | ||||
|             function () { | ||||
|                 this._key.press(); | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|             })); | ||||
|         button.connect('button-release-event', Lang.bind(this, | ||||
|             function () { | ||||
|                 this._key.release(); | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|             })); | ||||
|  | ||||
|         return button; | ||||
|     }, | ||||
| @@ -106,8 +114,16 @@ const Key = new Lang.Class({ | ||||
|             let label = this._getUnichar(extended_key); | ||||
|             let key = new St.Button({ label: label, style_class: 'keyboard-key' }); | ||||
|             key.extended_key = extended_key; | ||||
|             key.connect('button-press-event', Lang.bind(this, function () { extended_key.press(); })); | ||||
|             key.connect('button-release-event', Lang.bind(this, function () { extended_key.release(); })); | ||||
|             key.connect('button-press-event', Lang.bind(this, | ||||
|                 function () { | ||||
|                     extended_key.press(); | ||||
|                     return Clutter.EVENT_PROPAGATE; | ||||
|                 })); | ||||
|             key.connect('button-release-event', Lang.bind(this, | ||||
|                 function () { | ||||
|                     extended_key.release(); | ||||
|                     return Clutter.EVENT_PROPAGATE; | ||||
|                 })); | ||||
|             this._extended_keyboard.add(key); | ||||
|         } | ||||
|         this._boxPointer.bin.add_actor(this._extended_keyboard); | ||||
| @@ -252,7 +268,10 @@ const Keyboard = new Lang.Class({ | ||||
|  | ||||
|         if (!this._showIdleId) | ||||
|             this._showIdleId = GLib.idle_add(GLib.PRIORITY_DEFAULT_IDLE, | ||||
|                                              Lang.bind(this, function() { this.Show(time); })); | ||||
|                                              Lang.bind(this, function() { | ||||
|                                                  this.Show(time); | ||||
|                                                  return GLib.SOURCE_REMOVE; | ||||
|                                              })); | ||||
|     }, | ||||
|  | ||||
|     _createLayersForGroup: function (gname) { | ||||
| @@ -294,7 +313,7 @@ const Keyboard = new Lang.Class({ | ||||
|         else if (release && this._capturedPress) | ||||
|             this._hideSubkeys(); | ||||
|  | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _addRows : function (keys, layout) { | ||||
| @@ -438,7 +457,6 @@ const Keyboard = new Lang.Class({ | ||||
|     _createSource: function () { | ||||
|         if (this._source == null) { | ||||
|             this._source = new KeyboardSource(this); | ||||
|             this._source.setTransient(true); | ||||
|             Main.messageTray.add(this._source); | ||||
|         } | ||||
|     }, | ||||
| @@ -480,6 +498,7 @@ const Keyboard = new Lang.Class({ | ||||
|                                                    Lang.bind(this, function() { | ||||
|                                                        this._clearKeyboardRestTimer(); | ||||
|                                                        this._show(monitor); | ||||
|                                                        return GLib.SOURCE_REMOVE; | ||||
|                                                    })); | ||||
|     }, | ||||
|  | ||||
| @@ -505,6 +524,7 @@ const Keyboard = new Lang.Class({ | ||||
|                                                    Lang.bind(this, function() { | ||||
|                                                        this._clearKeyboardRestTimer(); | ||||
|                                                        this._hide(); | ||||
|                                                        return GLib.SOURCE_REMOVE; | ||||
|                                                    })); | ||||
|     }, | ||||
|  | ||||
|   | ||||
							
								
								
									
										132
									
								
								js/ui/layout.js
									
									
									
									
									
								
							
							
						
						
									
										132
									
								
								js/ui/layout.js
									
									
									
									
									
								
							| @@ -4,7 +4,6 @@ const Clutter = imports.gi.Clutter; | ||||
| const GLib = imports.gi.GLib; | ||||
| const GObject = imports.gi.GObject; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Signals = imports.signals; | ||||
| @@ -213,12 +212,21 @@ const LayoutManager = new Lang.Class({ | ||||
|         this.addChrome(this.trayBox); | ||||
|         this._setupTrayPressure(); | ||||
|  | ||||
|         this.modalDialogGroup = new St.Widget({ name: 'modalDialogGroup', | ||||
|                                                 layout_manager: new Clutter.BinLayout() }); | ||||
|         this.uiGroup.add_actor(this.modalDialogGroup); | ||||
|  | ||||
|         this.keyboardBox = new St.BoxLayout({ name: 'keyboardBox', | ||||
|                                               reactive: true, | ||||
|                                               track_hover: true }); | ||||
|         this.addChrome(this.keyboardBox); | ||||
|         this._keyboardHeightNotifyId = 0; | ||||
|  | ||||
|         // A dummy actor that tracks the mouse or text cursor, based on the | ||||
|         // position and size set in setDummyCursorGeometry. | ||||
|         this.dummyCursor = new St.Widget({ width: 0, height: 0 }); | ||||
|         this.uiGroup.add_actor(this.dummyCursor); | ||||
|  | ||||
|         global.stage.remove_actor(global.top_window_group); | ||||
|         this.uiGroup.add_actor(global.top_window_group); | ||||
|  | ||||
| @@ -352,26 +360,26 @@ const LayoutManager = new Lang.Class({ | ||||
|         this.emit('hot-corners-changed'); | ||||
|     }, | ||||
|  | ||||
|     _createBackground: function(monitorIndex) { | ||||
|     _addBackgroundMenu: function(bgManager) { | ||||
|         BackgroundMenu.addBackgroundMenu(bgManager.background.actor, this); | ||||
|     }, | ||||
|  | ||||
|     _createBackgroundManager: function(monitorIndex) { | ||||
|         let bgManager = new Background.BackgroundManager({ container: this._backgroundGroup, | ||||
|                                                            layoutManager: this, | ||||
|                                                            monitorIndex: monitorIndex }); | ||||
|         BackgroundMenu.addBackgroundMenu(bgManager.background.actor); | ||||
|  | ||||
|         bgManager.connect('changed', Lang.bind(this, function() { | ||||
|                               BackgroundMenu.addBackgroundMenu(bgManager.background.actor); | ||||
|                           })); | ||||
|         bgManager.connect('changed', Lang.bind(this, this._addBackgroundMenu)); | ||||
|         this._addBackgroundMenu(bgManager); | ||||
|  | ||||
|         this._bgManagers[monitorIndex] = bgManager; | ||||
|  | ||||
|         return bgManager.background; | ||||
|         return bgManager; | ||||
|     }, | ||||
|  | ||||
|     _createSecondaryBackgrounds: function() { | ||||
|     _showSecondaryBackgrounds: function() { | ||||
|         for (let i = 0; i < this.monitors.length; i++) { | ||||
|             if (i != this.primaryIndex) { | ||||
|                 let background = this._createBackground(i); | ||||
|  | ||||
|                 let background = this._bgManagers[i].background; | ||||
|                 background.actor.show(); | ||||
|                 background.actor.opacity = 0; | ||||
|                 Tweener.addTween(background.actor, | ||||
|                                  { opacity: 255, | ||||
| @@ -381,10 +389,6 @@ const LayoutManager = new Lang.Class({ | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _createPrimaryBackground: function() { | ||||
|         this._createBackground(this.primaryIndex); | ||||
|     }, | ||||
|  | ||||
|     _updateBackgrounds: function() { | ||||
|         let i; | ||||
|         for (i = 0; i < this._bgManagers.length; i++) | ||||
| @@ -395,11 +399,12 @@ const LayoutManager = new Lang.Class({ | ||||
|         if (Main.sessionMode.isGreeter) | ||||
|             return; | ||||
|  | ||||
|         if (this._startingUp) | ||||
|             return; | ||||
|  | ||||
|         for (let i = 0; i < this.monitors.length; i++) { | ||||
|             this._createBackground(i); | ||||
|             let bgManager = this._createBackgroundManager(i); | ||||
|             this._bgManagers.push(bgManager); | ||||
|  | ||||
|             if (i != this.primaryIndex && this._startingUp) | ||||
|                 bgManager.background.actor.hide(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -595,7 +600,7 @@ const LayoutManager = new Lang.Class({ | ||||
|         if (Main.sessionMode.isGreeter) { | ||||
|             this.panelBox.translation_y = -this.panelBox.height; | ||||
|         } else { | ||||
|             this._createPrimaryBackground(); | ||||
|             this._updateBackgrounds(); | ||||
|  | ||||
|             // We need to force an update of the regions now before we scale | ||||
|             // the UI group to get the coorect allocation for the struts. | ||||
| @@ -610,7 +615,7 @@ const LayoutManager = new Lang.Class({ | ||||
|  | ||||
|             this.uiGroup.set_pivot_point(x / global.screen_width, | ||||
|                                          y / global.screen_height); | ||||
|             this.uiGroup.scale_x = this.uiGroup.scale_y = 0.5; | ||||
|             this.uiGroup.scale_x = this.uiGroup.scale_y = 0.75; | ||||
|             this.uiGroup.opacity = 0; | ||||
|             global.window_group.set_clip(monitor.x, monitor.y, monitor.width, monitor.height); | ||||
|         } | ||||
| @@ -625,7 +630,7 @@ const LayoutManager = new Lang.Class({ | ||||
|         // when the system is bogged down | ||||
|         GLib.idle_add(GLib.PRIORITY_LOW, Lang.bind(this, function() { | ||||
|             this._startupAnimation(); | ||||
|             return false; | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
| @@ -673,7 +678,7 @@ const LayoutManager = new Lang.Class({ | ||||
|         this.keyboardBox.show(); | ||||
|  | ||||
|         if (!Main.sessionMode.isGreeter) { | ||||
|             this._createSecondaryBackgrounds(); | ||||
|             this._showSecondaryBackgrounds(); | ||||
|             global.window_group.remove_clip(); | ||||
|         } | ||||
|  | ||||
| @@ -723,6 +728,21 @@ const LayoutManager = new Lang.Class({ | ||||
|         this._updateRegions(); | ||||
|     }, | ||||
|  | ||||
|     // setDummyCursorGeometry: | ||||
|     // | ||||
|     // The cursor dummy is a standard widget commonly used for popup | ||||
|     // menus and box pointers to track, as the box pointer API only | ||||
|     // tracks actors. If you want to pop up a menu based on where the | ||||
|     // user clicked, or where the text cursor is, the cursor dummy | ||||
|     // is what you should use. Given that the menu should not track | ||||
|     // the actual mouse pointer as it moves, you need to call this | ||||
|     // function before you show the menu to ensure it is at the right | ||||
|     // position and has the right size. | ||||
|     setDummyCursorGeometry: function(x, y, w, h) { | ||||
|         this.dummyCursor.set_position(Math.round(x), Math.round(y)); | ||||
|         this.dummyCursor.set_size(Math.round(w), Math.round(h)); | ||||
|     }, | ||||
|  | ||||
|     // addChrome: | ||||
|     // @actor: an actor to add to the chrome | ||||
|     // @params: (optional) additional params | ||||
| @@ -814,13 +834,12 @@ const LayoutManager = new Lang.Class({ | ||||
|  | ||||
|         let actorData = Params.parse(params, defaultParams); | ||||
|         actorData.actor = actor; | ||||
|         actorData.isToplevel = actor.get_parent() == this.uiGroup; | ||||
|         actorData.visibleId = actor.connect('notify::visible', | ||||
|                                             Lang.bind(this, this._queueUpdateRegions)); | ||||
|         actorData.allocationId = actor.connect('notify::allocation', | ||||
|                                                Lang.bind(this, this._queueUpdateRegions)); | ||||
|         actorData.parentSetId = actor.connect('parent-set', | ||||
|                                               Lang.bind(this, this._actorReparented)); | ||||
|         actorData.destroyId = actor.connect('destroy', | ||||
|                                             Lang.bind(this, this._untrackActor)); | ||||
|         // Note that destroying actor will unset its parent, so we don't | ||||
|         // need to connect to 'destroy' too. | ||||
|  | ||||
| @@ -838,22 +857,11 @@ const LayoutManager = new Lang.Class({ | ||||
|         this._trackedActors.splice(i, 1); | ||||
|         actor.disconnect(actorData.visibleId); | ||||
|         actor.disconnect(actorData.allocationId); | ||||
|         actor.disconnect(actorData.parentSetId); | ||||
|         actor.disconnect(actorData.destroyId); | ||||
|  | ||||
|         this._queueUpdateRegions(); | ||||
|     }, | ||||
|  | ||||
|     _actorReparented: function(actor, oldParent) { | ||||
|         let newParent = actor.get_parent(); | ||||
|         if (!newParent) { | ||||
|             this._untrackActor(actor); | ||||
|         } else { | ||||
|             let i = this._findActor(actor); | ||||
|             let actorData = this._trackedActors[i]; | ||||
|             actorData.isToplevel = (newParent == this.uiGroup); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _updateVisibility: function() { | ||||
|         let windowsVisible = Main.sessionMode.hasWindows && !this._inOverview; | ||||
|  | ||||
| @@ -864,8 +872,6 @@ const LayoutManager = new Lang.Class({ | ||||
|             let actorData = this._trackedActors[i], visible; | ||||
|             if (!actorData.trackFullscreen) | ||||
|                 continue; | ||||
|             if (!actorData.isToplevel) | ||||
|                 continue; | ||||
|  | ||||
|             if (!windowsVisible) | ||||
|                 visible = true; | ||||
| @@ -905,8 +911,8 @@ const LayoutManager = new Lang.Class({ | ||||
|             return; | ||||
|  | ||||
|         if (!this._updateRegionIdle) | ||||
|             this._updateRegionIdle = Mainloop.idle_add(Lang.bind(this, this._updateRegions), | ||||
|                                                        Meta.PRIORITY_BEFORE_REDRAW); | ||||
|             this._updateRegionIdle = Meta.later_add(Meta.LaterType.BEFORE_REDRAW, | ||||
|                                                     Lang.bind(this, this._updateRegions)); | ||||
|     }, | ||||
|  | ||||
|     _getWindowActorsForWorkspace: function(workspace) { | ||||
| @@ -937,7 +943,7 @@ const LayoutManager = new Lang.Class({ | ||||
|         let rects = [], struts = [], i; | ||||
|  | ||||
|         if (this._updateRegionIdle) { | ||||
|             Mainloop.source_remove(this._updateRegionIdle); | ||||
|             Meta.later_remove(this._updateRegionIdle); | ||||
|             delete this._updateRegionIdle; | ||||
|         } | ||||
|  | ||||
| @@ -1011,19 +1017,39 @@ const LayoutManager = new Lang.Class({ | ||||
|                     continue; | ||||
|  | ||||
|                 // Ensure that the strut rects goes all the way to the screen edge, | ||||
|                 // as this really what mutter expects. | ||||
|                 // as this really what mutter expects. However skip this step | ||||
|                 // in cases where this would render an entire monitor unusable. | ||||
|                 switch (side) { | ||||
|                 case Meta.Side.TOP: | ||||
|                     y1 = 0; | ||||
|                     let hasMonitorsAbove = this.monitors.some(Lang.bind(this, | ||||
|                         function(mon) { | ||||
|                             return this._isAboveOrBelowPrimary(mon) && | ||||
|                                    mon.y < primary.y; | ||||
|                         })); | ||||
|                     if (!hasMonitorsAbove) | ||||
|                         y1 = 0; | ||||
|                     break; | ||||
|                 case Meta.Side.BOTTOM: | ||||
|                     y2 = global.screen_height; | ||||
|                     if (this.primaryIndex == this.bottomIndex) | ||||
|                         y2 = global.screen_height; | ||||
|                     break; | ||||
|                 case Meta.Side.LEFT: | ||||
|                     x1 = 0; | ||||
|                     let hasMonitorsLeft = this.monitors.some(Lang.bind(this, | ||||
|                         function(mon) { | ||||
|                             return !this._isAboveOrBelowPrimary(mon) && | ||||
|                                    mon.x < primary.x; | ||||
|                         })); | ||||
|                     if (!hasMonitorsLeft) | ||||
|                         x1 = 0; | ||||
|                     break; | ||||
|                 case Meta.Side.RIGHT: | ||||
|                     x2 = global.screen_width; | ||||
|                     let hasMonitorsRight = this.monitors.some(Lang.bind(this, | ||||
|                         function(mon) { | ||||
|                             return !this._isAboveOrBelowPrimary(mon) && | ||||
|                                    mon.x > primary.x; | ||||
|                         })); | ||||
|                     if (!hasMonitorsRight) | ||||
|                         x2 = global.screen_width; | ||||
|                     break; | ||||
|                 } | ||||
|  | ||||
| @@ -1042,7 +1068,7 @@ const LayoutManager = new Lang.Class({ | ||||
|             workspace.set_builtin_struts(struts); | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(LayoutManager.prototype); | ||||
| @@ -1229,20 +1255,20 @@ const HotCorner = new Lang.Class({ | ||||
|             this._entered = true; | ||||
|             this._toggleOverview(); | ||||
|         } | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onCornerLeft : function(actor, event) { | ||||
|         if (event.get_related() != this.actor) | ||||
|             this._entered = false; | ||||
|         // Consume event, otherwise this will confuse onEnvironsLeft | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _onEnvironsLeft : function(actor, event) { | ||||
|         if (event.get_related() != this._corner) | ||||
|             this._entered = false; | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -5,11 +5,67 @@ const Lang = imports.lang; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Signals = imports.signals; | ||||
| const St = imports.gi.St; | ||||
| const Shell = imports.gi.Shell; | ||||
|  | ||||
| const Params = imports.misc.params; | ||||
| const Tweener = imports.ui.tweener; | ||||
|  | ||||
| const DEFAULT_FADE_FACTOR = 0.4; | ||||
| const VIGNETTE_BRIGHTNESS = 0.8; | ||||
| const VIGNETTE_SHARPNESS = 0.7; | ||||
|  | ||||
| const VIGNETTE_DECLARATIONS = '\ | ||||
| uniform float brightness;\n\ | ||||
| uniform float vignette_sharpness;\n'; | ||||
|  | ||||
| const VIGNETTE_CODE = '\ | ||||
| cogl_color_out.a = cogl_color_in.a;\n\ | ||||
| cogl_color_out.rgb = vec3(0.0, 0.0, 0.0);\n\ | ||||
| vec2 position = cogl_tex_coord_in[0].xy - 0.5;\n\ | ||||
| float t = length(2.0 * position);\n\ | ||||
| t = clamp(t, 0.0, 1.0);\n\ | ||||
| float pixel_brightness = mix(1.0, 1.0 - vignette_sharpness, t);\n\ | ||||
| cogl_color_out.a = cogl_color_out.a * (1 - pixel_brightness * brightness);'; | ||||
|  | ||||
| const RadialShaderQuad = new Lang.Class({ | ||||
|     Name: 'RadialShaderQuad', | ||||
|     Extends: Shell.GLSLQuad, | ||||
|  | ||||
|     _init: function(params) { | ||||
|         this.parent(params); | ||||
|  | ||||
|         this._brightnessLocation = this.get_uniform_location('brightness'); | ||||
|         this._sharpnessLocation = this.get_uniform_location('vignette_sharpness'); | ||||
|  | ||||
|         this.brightness = 1.0; | ||||
|         this.vignetteSharpness = 0.0; | ||||
|     }, | ||||
|  | ||||
|     vfunc_build_pipeline: function() { | ||||
|         this.add_glsl_snippet(Shell.SnippetHook.FRAGMENT, | ||||
|                               VIGNETTE_DECLARATIONS, VIGNETTE_CODE, true); | ||||
|     }, | ||||
|  | ||||
|     get brightness() { | ||||
|         return this._brightness; | ||||
|     }, | ||||
|  | ||||
|     set brightness(v) { | ||||
|         this._brightness = v; | ||||
|         this.set_uniform_float(this._brightnessLocation, | ||||
|                                1, [this._brightness]); | ||||
|     }, | ||||
|  | ||||
|     get vignetteSharpness() { | ||||
|         return this._sharpness; | ||||
|     }, | ||||
|  | ||||
|     set vignetteSharpness(v) { | ||||
|         this._sharpness = v; | ||||
|         this.set_uniform_float(this._sharpnessLocation, | ||||
|                                1, [this._sharpness]); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| /** | ||||
|  * Lightbox: | ||||
| @@ -43,15 +99,23 @@ const Lightbox = new Lang.Class({ | ||||
|                                         width: null, | ||||
|                                         height: null, | ||||
|                                         fadeFactor: DEFAULT_FADE_FACTOR, | ||||
|                                         radialEffect: false, | ||||
|                                       }); | ||||
|  | ||||
|         this._container = container; | ||||
|         this._children = container.get_children(); | ||||
|         this._fadeFactor = params.fadeFactor; | ||||
|         this.actor = new St.Bin({ x: 0, | ||||
|                                   y: 0, | ||||
|                                   style_class: 'lightbox', | ||||
|                                   reactive: params.inhibitEvents }); | ||||
|         this._radialEffect = Clutter.feature_available(Clutter.FeatureFlags.SHADERS_GLSL) && params.radialEffect; | ||||
|         if (this._radialEffect) | ||||
|             this.actor = new RadialShaderQuad({ x: 0, | ||||
|                                                 y: 0, | ||||
|                                                 reactive: params.inhibitEvents }); | ||||
|         else | ||||
|             this.actor = new St.Bin({ x: 0, | ||||
|                                       y: 0, | ||||
|                                       opacity: 0, | ||||
|                                       style_class: 'lightbox', | ||||
|                                       reactive: params.inhibitEvents }); | ||||
|  | ||||
|         container.add_actor(this.actor); | ||||
|         this.actor.raise_top(); | ||||
| @@ -101,9 +165,18 @@ const Lightbox = new Lang.Class({ | ||||
|         fadeInTime = fadeInTime || 0; | ||||
|  | ||||
|         Tweener.removeTweens(this.actor); | ||||
|         if (fadeInTime != 0) { | ||||
|             this.shown = false; | ||||
|             this.actor.opacity = 0; | ||||
|         if (this._radialEffect) { | ||||
|             Tweener.addTween(this.actor, | ||||
|                              { brightness: VIGNETTE_BRIGHTNESS, | ||||
|                                vignetteSharpness: VIGNETTE_SHARPNESS, | ||||
|                                time: fadeInTime, | ||||
|                                transition: 'easeOutQuad', | ||||
|                                onComplete: Lang.bind(this, function() { | ||||
|                                    this.shown = true; | ||||
|                                    this.emit('shown'); | ||||
|                                }) | ||||
|                              }); | ||||
|         } else { | ||||
|             Tweener.addTween(this.actor, | ||||
|                              { opacity: 255 * this._fadeFactor, | ||||
|                                time: fadeInTime, | ||||
| @@ -113,11 +186,8 @@ const Lightbox = new Lang.Class({ | ||||
|                                    this.emit('shown'); | ||||
|                                }) | ||||
|                              }); | ||||
|         } else { | ||||
|             this.actor.opacity = 255 * this._fadeFactor; | ||||
|             this.shown = true; | ||||
|             this.emit('shown'); | ||||
|         } | ||||
|  | ||||
|         this.actor.show(); | ||||
|     }, | ||||
|  | ||||
| @@ -126,7 +196,18 @@ const Lightbox = new Lang.Class({ | ||||
|  | ||||
|         this.shown = false; | ||||
|         Tweener.removeTweens(this.actor); | ||||
|         if (fadeOutTime != 0) { | ||||
|         if (this._radialEffect) { | ||||
|             Tweener.addTween(this.actor, | ||||
|                              { brightness: 1.0, | ||||
|                                vignetteSharpness: 0.0, | ||||
|                                opacity: 0, | ||||
|                                time: fadeOutTime, | ||||
|                                transition: 'easeOutQuad', | ||||
|                                onComplete: Lang.bind(this, function() { | ||||
|                                    this.actor.hide(); | ||||
|                                }) | ||||
|                              }); | ||||
|         } else { | ||||
|             Tweener.addTween(this.actor, | ||||
|                              { opacity: 0, | ||||
|                                time: fadeOutTime, | ||||
| @@ -135,8 +216,6 @@ const Lightbox = new Lang.Class({ | ||||
|                                    this.actor.hide(); | ||||
|                                }) | ||||
|                              }); | ||||
|         } else { | ||||
|             this.actor.hide(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|   | ||||
| @@ -27,6 +27,8 @@ const CHEVRON = '>>> '; | ||||
| /* Imports...feel free to add here as needed */ | ||||
| var commandHeader = 'const Clutter = imports.gi.Clutter; ' + | ||||
|                     'const GLib = imports.gi.GLib; ' + | ||||
|                     'const GObject = imports.gi.GObject; ' + | ||||
|                     'const Gio = imports.gi.Gio; ' + | ||||
|                     'const Gtk = imports.gi.Gtk; ' + | ||||
|                     'const Mainloop = imports.mainloop; ' + | ||||
|                     'const Meta = imports.gi.Meta; ' + | ||||
| @@ -109,6 +111,7 @@ const AutoComplete = new Lang.Class({ | ||||
|             } | ||||
|             this._lastTabTime = currTime; | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     // Insert characters of text not already included in head at cursor position.  i.e., if text="abc" and head="a", | ||||
| @@ -558,7 +561,7 @@ const Inspector = new Lang.Class({ | ||||
|     _onKeyPressEvent: function (actor, event) { | ||||
|         if (event.get_key_symbol() == Clutter.Escape) | ||||
|             this._close(); | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _onButtonPressEvent: function (actor, event) { | ||||
| @@ -567,7 +570,7 @@ const Inspector = new Lang.Class({ | ||||
|             this.emit('target', this._target, stageX, stageY); | ||||
|         } | ||||
|         this._close(); | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _onScrollEvent: function (actor, event) { | ||||
| @@ -601,12 +604,12 @@ const Inspector = new Lang.Class({ | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _onMotionEvent: function (actor, event) { | ||||
|         this._update(event); | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _update: function(event) { | ||||
| @@ -669,13 +672,13 @@ const Extensions = new Lang.Class({ | ||||
|     _onViewSource: function (actor) { | ||||
|         let extension = actor._extension; | ||||
|         let uri = extension.dir.get_uri(); | ||||
|         Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context()); | ||||
|         Gio.app_info_launch_default_for_uri(uri, global.create_app_launch_context(0, -1)); | ||||
|         this._lookingGlass.close(); | ||||
|     }, | ||||
|  | ||||
|     _onWebPage: function (actor) { | ||||
|         let extension = actor._extension; | ||||
|         Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context()); | ||||
|         Gio.app_info_launch_default_for_uri(extension.metadata.url, global.create_app_launch_context(0, -1)); | ||||
|         this._lookingGlass.close(); | ||||
|     }, | ||||
|  | ||||
| @@ -828,7 +831,7 @@ const LookingGlass = new Lang.Class({ | ||||
|                 global.stage.set_key_focus(this._entry); | ||||
|             })); | ||||
|             this.actor.hide(); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         })); | ||||
|  | ||||
|         let gcIcon = new St.Icon({ icon_name: 'gnome-fs-trash-full', | ||||
| @@ -841,7 +844,9 @@ const LookingGlass = new Lang.Class({ | ||||
|            this._timeoutId = Mainloop.timeout_add(500, Lang.bind(this, function () { | ||||
|                 gcIcon.icon_name = 'gnome-fs-trash-full'; | ||||
|                 Mainloop.source_remove(this._timeoutId); | ||||
|                 return GLib.SOURCE_REMOVE; | ||||
|            })); | ||||
|            return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|  | ||||
|         let notebook = new Notebook(); | ||||
| @@ -1063,7 +1068,7 @@ const LookingGlass = new Lang.Class({ | ||||
|             } else { | ||||
|                 this.close(); | ||||
|             } | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|         // Ctrl+PgUp and Ctrl+PgDown switches tabs in the notebook view | ||||
|         if (modifierState & Clutter.ModifierType.CONTROL_MASK) { | ||||
| @@ -1073,7 +1078,7 @@ const LookingGlass = new Lang.Class({ | ||||
|                 this._notebook.nextTab(); | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     open : function() { | ||||
|   | ||||
| @@ -11,6 +11,7 @@ const Mainloop = imports.mainloop; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Background = imports.ui.background; | ||||
| const FocusCaretTracker = imports.ui.focusCaretTracker; | ||||
| const Main = imports.ui.main; | ||||
| const MagnifierDBus = imports.ui.magnifierDBus; | ||||
| @@ -57,20 +58,6 @@ const Magnifier = new Lang.Class({ | ||||
|         // Magnifier is a manager of ZoomRegions. | ||||
|         this._zoomRegions = []; | ||||
|  | ||||
|         // Export to dbus. | ||||
|         magDBusService = new MagnifierDBus.ShellMagnifier(); | ||||
|  | ||||
|         let showAtLaunch = this._settingsInit(); | ||||
|         this.setActive(showAtLaunch); | ||||
|     }, | ||||
|  | ||||
|     _initialize: function() { | ||||
|         if (this._initialized) | ||||
|             return; | ||||
|         this._initialized = true; | ||||
|  | ||||
|         this._settingsInitLate(); | ||||
|  | ||||
|         // Create small clutter tree for the magnified mouse. | ||||
|         let cursorTracker = Meta.CursorTracker.get_for_screen(global.screen); | ||||
|         this._mouseSprite = new Clutter.Texture(); | ||||
| @@ -86,11 +73,15 @@ const Magnifier = new Lang.Class({ | ||||
|  | ||||
|         let aZoomRegion = new ZoomRegion(this, this._cursorRoot); | ||||
|         this._zoomRegions.push(aZoomRegion); | ||||
|         this._settingsInitRegion(aZoomRegion); | ||||
|         let showAtLaunch = this._settingsInit(aZoomRegion); | ||||
|         aZoomRegion.scrollContentsTo(this.xMouse, this.yMouse); | ||||
|  | ||||
|         cursorTracker.connect('cursor-changed', Lang.bind(this, this._updateMouseSprite)); | ||||
|         this._cursorTracker = cursorTracker; | ||||
|  | ||||
|         // Export to dbus. | ||||
|         magDBusService = new MagnifierDBus.ShellMagnifier(); | ||||
|         this.setActive(showAtLaunch); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
| @@ -115,20 +106,21 @@ const Magnifier = new Lang.Class({ | ||||
|      * @activate:   Boolean to activate or de-activate the magnifier. | ||||
|      */ | ||||
|     setActive: function(activate) { | ||||
|         if (activate == this.isActive()) | ||||
|             return; | ||||
|  | ||||
|         if (activate) | ||||
|             this._initialize(); | ||||
|         let isActive = this.isActive(); | ||||
|  | ||||
|         this._zoomRegions.forEach (function(zoomRegion, index, array) { | ||||
|             zoomRegion.setActive(activate); | ||||
|         }); | ||||
|  | ||||
|         if (activate) | ||||
|             this.startTrackingMouse(); | ||||
|         else | ||||
|             this.stopTrackingMouse(); | ||||
|         if (isActive != activate) { | ||||
|             if (activate) { | ||||
|                 Meta.disable_unredirect_for_screen(global.screen); | ||||
|                 this.startTrackingMouse(); | ||||
|             } else { | ||||
|                 Meta.enable_unredirect_for_screen(global.screen); | ||||
|                 this.stopTrackingMouse(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         // Make sure system mouse pointer is shown when all zoom regions are | ||||
|         // invisible. | ||||
| @@ -448,68 +440,64 @@ const Magnifier = new Lang.Class({ | ||||
|         this._mouseSprite.set_anchor_point(xHot, yHot); | ||||
|     }, | ||||
|  | ||||
|     _settingsInitRegion: function(zoomRegion) { | ||||
|         // Mag factor is accurate to two decimal places. | ||||
|         let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2)); | ||||
|         if (aPref != 0.0) | ||||
|             zoomRegion.setMagFactor(aPref, aPref); | ||||
|  | ||||
|         aPref = this._settings.get_enum(SCREEN_POSITION_KEY); | ||||
|         if (aPref) | ||||
|             zoomRegion.setScreenPosition(aPref); | ||||
|  | ||||
|         zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY)); | ||||
|         zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY)); | ||||
|  | ||||
|         aPref = this._settings.get_enum(MOUSE_TRACKING_KEY); | ||||
|         if (aPref) | ||||
|             zoomRegion.setMouseTrackingMode(aPref); | ||||
|  | ||||
|         aPref = this._settings.get_enum(FOCUS_TRACKING_KEY); | ||||
|         if (aPref) | ||||
|             zoomRegion.setFocusTrackingMode(aPref); | ||||
|  | ||||
|         aPref = this._settings.get_enum(CARET_TRACKING_KEY); | ||||
|         if (aPref) | ||||
|             zoomRegion.setCaretTrackingMode(aPref); | ||||
|  | ||||
|         aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY); | ||||
|         if (aPref) | ||||
|             zoomRegion.setInvertLightness(aPref); | ||||
|  | ||||
|         aPref = this._settings.get_double(COLOR_SATURATION_KEY); | ||||
|         if (aPref) | ||||
|             zoomRegion.setColorSaturation(aPref); | ||||
|  | ||||
|         let bc = {}; | ||||
|         bc.r = this._settings.get_double(BRIGHT_RED_KEY); | ||||
|         bc.g = this._settings.get_double(BRIGHT_GREEN_KEY); | ||||
|         bc.b = this._settings.get_double(BRIGHT_BLUE_KEY); | ||||
|         zoomRegion.setBrightness(bc); | ||||
|  | ||||
|         bc.r = this._settings.get_double(CONTRAST_RED_KEY); | ||||
|         bc.g = this._settings.get_double(CONTRAST_GREEN_KEY); | ||||
|         bc.b = this._settings.get_double(CONTRAST_BLUE_KEY); | ||||
|         zoomRegion.setContrast(bc); | ||||
|     }, | ||||
|  | ||||
|     _settingsInit: function() { | ||||
|     _settingsInit: function(zoomRegion) { | ||||
|         this._appSettings = new Gio.Settings({ schema: APPLICATIONS_SCHEMA }); | ||||
|         this._settings = new Gio.Settings({ schema: MAGNIFIER_SCHEMA }); | ||||
|  | ||||
|         this._appSettings.connect('changed::' + SHOW_KEY, Lang.bind(this, function() { | ||||
|             let active = this._appSettings.get_boolean(SHOW_KEY); | ||||
|             this.setActive(active); | ||||
|         })); | ||||
|         if (zoomRegion) { | ||||
|             // Mag factor is accurate to two decimal places. | ||||
|             let aPref = parseFloat(this._settings.get_double(MAG_FACTOR_KEY).toFixed(2)); | ||||
|             if (aPref != 0.0) | ||||
|                 zoomRegion.setMagFactor(aPref, aPref); | ||||
|  | ||||
|         return this._appSettings.get_boolean(SHOW_KEY); | ||||
|     }, | ||||
|             aPref = this._settings.get_enum(SCREEN_POSITION_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setScreenPosition(aPref); | ||||
|  | ||||
|             zoomRegion.setLensMode(this._settings.get_boolean(LENS_MODE_KEY)); | ||||
|             zoomRegion.setClampScrollingAtEdges(!this._settings.get_boolean(CLAMP_MODE_KEY)); | ||||
|  | ||||
|             aPref = this._settings.get_enum(MOUSE_TRACKING_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setMouseTrackingMode(aPref); | ||||
|  | ||||
|             aPref = this._settings.get_enum(FOCUS_TRACKING_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setFocusTrackingMode(aPref); | ||||
|  | ||||
|             aPref = this._settings.get_enum(CARET_TRACKING_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setCaretTrackingMode(aPref); | ||||
|  | ||||
|             aPref = this._settings.get_boolean(INVERT_LIGHTNESS_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setInvertLightness(aPref); | ||||
|  | ||||
|             aPref = this._settings.get_double(COLOR_SATURATION_KEY); | ||||
|             if (aPref) | ||||
|                 zoomRegion.setColorSaturation(aPref); | ||||
|  | ||||
|             let bc = {}; | ||||
|             bc.r = this._settings.get_double(BRIGHT_RED_KEY); | ||||
|             bc.g = this._settings.get_double(BRIGHT_GREEN_KEY); | ||||
|             bc.b = this._settings.get_double(BRIGHT_BLUE_KEY); | ||||
|             zoomRegion.setBrightness(bc); | ||||
|  | ||||
|             bc.r = this._settings.get_double(CONTRAST_RED_KEY); | ||||
|             bc.g = this._settings.get_double(CONTRAST_GREEN_KEY); | ||||
|             bc.b = this._settings.get_double(CONTRAST_BLUE_KEY); | ||||
|             zoomRegion.setContrast(bc); | ||||
|         } | ||||
|  | ||||
|     _settingsInitLate: function() { | ||||
|         let showCrosshairs = this._settings.get_boolean(SHOW_CROSS_HAIRS_KEY); | ||||
|         this.addCrosshairs(); | ||||
|         this.setCrosshairsVisible(showCrosshairs); | ||||
|  | ||||
|         this._appSettings.connect('changed::' + SHOW_KEY, | ||||
|                                   Lang.bind(this, function() { | ||||
|             this.setActive(this._appSettings.get_boolean(SHOW_KEY)); | ||||
|         })); | ||||
|  | ||||
|         this._settings.connect('changed::' + SCREEN_POSITION_KEY, | ||||
|                                Lang.bind(this, this._updateScreenPosition)); | ||||
|         this._settings.connect('changed::' + MAG_FACTOR_KEY, | ||||
| @@ -573,6 +561,7 @@ const Magnifier = new Lang.Class({ | ||||
|                                Lang.bind(this, function() { | ||||
|             this.setCrosshairsClip(this._settings.get_boolean(CROSS_HAIRS_CLIP_KEY)); | ||||
|         })); | ||||
|         return this._appSettings.get_boolean(SHOW_KEY); | ||||
|    }, | ||||
|  | ||||
|     _updateScreenPosition: function() { | ||||
| @@ -731,8 +720,16 @@ const ZoomRegion = new Lang.Class({ | ||||
|         let component = event.source.get_component_iface(); | ||||
|         if (!component || event.detail1 != 1) | ||||
|             return; | ||||
|         let extents = component.get_extents(Atspi.CoordType.SCREEN); | ||||
|         [this._xFocus, this._yFocus] = [extents.x, extents.y] | ||||
|         let extents; | ||||
|         try { | ||||
|             extents = component.get_extents(Atspi.CoordType.SCREEN); | ||||
|         } catch(e) { | ||||
|             log('Failed to read extents of focused component: ' + e.message); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         [this._xFocus, this._yFocus] = [extents.x + (extents.width / 2), | ||||
|                                         extents.y + (extents.height / 2)]; | ||||
|         this._centerFromFocusPosition(); | ||||
|     }, | ||||
|  | ||||
| @@ -740,7 +737,14 @@ const ZoomRegion = new Lang.Class({ | ||||
|         let text = event.source.get_text_iface(); | ||||
|         if (!text) | ||||
|             return; | ||||
|         let extents = text.get_character_extents(text.get_caret_offset(), 0); | ||||
|         let extents; | ||||
|         try { | ||||
|             extents = text.get_character_extents(text.get_caret_offset(), 0); | ||||
|         } catch(e) { | ||||
|             log('Failed to read extents of text caret: ' + e.message); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         [this._xCaret, this._yCaret] = [extents.x, extents.y]; | ||||
|         this._centerFromCaretPosition(); | ||||
|     }, | ||||
| @@ -763,6 +767,9 @@ const ZoomRegion = new Lang.Class({ | ||||
|         } else { | ||||
|             this._destroyActors(); | ||||
|         } | ||||
|  | ||||
|         this._syncCaretTracking(); | ||||
|         this._syncFocusTracking(); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
| @@ -822,10 +829,7 @@ const ZoomRegion = new Lang.Class({ | ||||
|      */ | ||||
|     setFocusTrackingMode: function(mode) { | ||||
|         this._focusTrackingMode = mode; | ||||
|         if (this._focusTrackingMode == GDesktopEnums.MagnifierFocusTrackingMode.NONE) | ||||
|             this._focusCaretTracker.deregisterFocusListener(); | ||||
|         else | ||||
|             this._focusCaretTracker.registerFocusListener(); | ||||
|         this._syncFocusTracking(); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
| @@ -834,10 +838,27 @@ const ZoomRegion = new Lang.Class({ | ||||
|      */ | ||||
|     setCaretTrackingMode: function(mode) { | ||||
|         this._caretTrackingMode = mode; | ||||
|         if (this._caretTrackingMode == GDesktopEnums.MagnifierCaretTrackingMode.NONE) | ||||
|             this._focusCaretTracker.deregisterCaretListener(); | ||||
|         this._syncCaretTracking(); | ||||
|     }, | ||||
|  | ||||
|     _syncFocusTracking: function() { | ||||
|         let enabled = this._focusTrackingMode != GDesktopEnums.MagnifierFocusTrackingMode.NONE && | ||||
|             this.isActive(); | ||||
|  | ||||
|         if (enabled) | ||||
|             this._focusCaretTracker.registerFocusListener(); | ||||
|         else | ||||
|             this._focusCaretTracker.deregisterFocusListener(); | ||||
|     }, | ||||
|  | ||||
|     _syncCaretTracking: function() { | ||||
|         let enabled = this._caretTrackingMode != GDesktopEnums.MagnifierCaretTrackingMode.NONE && | ||||
|             this.isActive(); | ||||
|  | ||||
|         if (enabled) | ||||
|             this._focusCaretTracker.registerCaretListener(); | ||||
|         else | ||||
|             this._focusCaretTracker.deregisterCaretListener(); | ||||
|     }, | ||||
|  | ||||
|     /** | ||||
| @@ -1178,13 +1199,17 @@ const ZoomRegion = new Lang.Class({ | ||||
|         // Add a background for when the magnified uiGroup is scrolled | ||||
|         // out of view (don't want to see desktop showing through). | ||||
|         this._background = new Clutter.Actor({ background_color: Main.DEFAULT_BACKGROUND_COLOR, | ||||
|                                                layout_manager: new Clutter.BinLayout(), | ||||
|                                                width: global.screen_width, | ||||
|                                                height: global.screen_height }); | ||||
|         let noiseTexture = (new Background.SystemBackground()).actor; | ||||
|         this._background.add_actor(noiseTexture); | ||||
|         mainGroup.add_actor(this._background); | ||||
|  | ||||
|         // Clone the group that contains all of UI on the screen.  This is the | ||||
|         // chrome, the windows, etc. | ||||
|         this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup }); | ||||
|         this._uiGroupClone = new Clutter.Clone({ source: Main.uiGroup, | ||||
|                                                  clip_to_allocation: true }); | ||||
|         mainGroup.add_actor(this._uiGroupClone); | ||||
|  | ||||
|         // Add either the given mouseSourceActor to the ZoomRegion, or a clone of | ||||
|   | ||||
| @@ -609,7 +609,7 @@ function queueDeferredWork(workId) { | ||||
|         _deferredTimeoutId = Mainloop.timeout_add_seconds(DEFERRED_TIMEOUT_SECONDS, function () { | ||||
|             _runAllDeferredWork(); | ||||
|             _deferredTimeoutId = 0; | ||||
|             return false; | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         }); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -19,7 +19,6 @@ const BoxPointer = imports.ui.boxpointer; | ||||
| const CtrlAltTab = imports.ui.ctrlAltTab; | ||||
| const GnomeSession = imports.misc.gnomeSession; | ||||
| const GrabHelper = imports.ui.grabHelper; | ||||
| const Hash = imports.misc.hash; | ||||
| const Lightbox = imports.ui.lightbox; | ||||
| const Main = imports.ui.main; | ||||
| const PointerWatcher = imports.ui.pointerWatcher; | ||||
| @@ -76,7 +75,7 @@ const Urgency = { | ||||
|     NORMAL: 1, | ||||
|     HIGH: 2, | ||||
|     CRITICAL: 3 | ||||
| } | ||||
| }; | ||||
|  | ||||
| function _fixMarkup(text, allowMarkup) { | ||||
|     if (allowMarkup) { | ||||
| @@ -187,7 +186,7 @@ const URLHighlighter = new Lang.Class({ | ||||
|             // The MessageTray doesn't actually hide us, so | ||||
|             // we need to check for paint opacities as well. | ||||
|             if (!actor.visible || actor.get_paint_opacity() == 0) | ||||
|                 return false; | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             // Keep Notification.actor from seeing this and taking | ||||
|             // a pointer grab, which would block our button-release-event | ||||
| @@ -196,7 +195,7 @@ const URLHighlighter = new Lang.Class({ | ||||
|         })); | ||||
|         this.actor.connect('button-release-event', Lang.bind(this, function (actor, event) { | ||||
|             if (!actor.visible || actor.get_paint_opacity() == 0) | ||||
|                 return false; | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             let urlId = this._findUrlAtPos(event); | ||||
|             if (urlId != -1) { | ||||
| @@ -204,14 +203,14 @@ const URLHighlighter = new Lang.Class({ | ||||
|                 if (url.indexOf(':') == -1) | ||||
|                     url = 'http://' + url; | ||||
|  | ||||
|                 Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context()); | ||||
|                 return true; | ||||
|                 Gio.app_info_launch_default_for_uri(url, global.create_app_launch_context(0, -1)); | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|         this.actor.connect('motion-event', Lang.bind(this, function(actor, event) { | ||||
|             if (!actor.visible || actor.get_paint_opacity() == 0) | ||||
|                 return false; | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             let urlId = this._findUrlAtPos(event); | ||||
|             if (urlId != -1 && !this._cursorChanged) { | ||||
| @@ -221,16 +220,17 @@ const URLHighlighter = new Lang.Class({ | ||||
|                 global.screen.set_cursor(Meta.Cursor.DEFAULT); | ||||
|                 this._cursorChanged = false; | ||||
|             } | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|         this.actor.connect('leave-event', Lang.bind(this, function() { | ||||
|             if (!this.actor.visible || this.actor.get_paint_opacity() == 0) | ||||
|                 return; | ||||
|                 return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|             if (this._cursorChanged) { | ||||
|                 this._cursorChanged = false; | ||||
|                 global.screen.set_cursor(Meta.Cursor.DEFAULT); | ||||
|             } | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
| @@ -1001,6 +1001,9 @@ const Notification = new Lang.Class({ | ||||
|             Meta.later_add(Meta.LaterType.BEFORE_REDRAW, | ||||
|                            Lang.bind(this, | ||||
|                                      function() { | ||||
|                                          if (this._destroyed) | ||||
|                                              return false; | ||||
|  | ||||
|                                         if (this._canExpandContent()) { | ||||
|                                             this._addBannerBody(); | ||||
|                                             this._table.add_style_class_name('multi-line-notification'); | ||||
| @@ -1159,32 +1162,12 @@ const SourceActor = new Lang.Class({ | ||||
|         })); | ||||
|         this._actorDestroyed = false; | ||||
|  | ||||
|         this._counterLabel = new St.Label( {x_align: Clutter.ActorAlign.CENTER, | ||||
|                                             x_expand: true, | ||||
|                                             y_align: Clutter.ActorAlign.CENTER, | ||||
|                                             y_expand: true }); | ||||
|  | ||||
|         this._counterBin = new St.Bin({ style_class: 'summary-source-counter', | ||||
|                                         child: this._counterLabel, | ||||
|                                         layout_manager: new Clutter.BinLayout() }); | ||||
|         this._counterBin.hide(); | ||||
|  | ||||
|         this._counterBin.connect('style-changed', Lang.bind(this, function() { | ||||
|             let themeNode = this._counterBin.get_theme_node(); | ||||
|             this._counterBin.translation_x = themeNode.get_length('-shell-counter-overlap-x'); | ||||
|             this._counterBin.translation_y = themeNode.get_length('-shell-counter-overlap-y'); | ||||
|         })); | ||||
|  | ||||
|         this._iconBin = new St.Bin({ width: size, | ||||
|                                      height: size, | ||||
|                                      x_fill: true, | ||||
|                                      y_fill: true }); | ||||
|         let scale_factor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|         this._iconBin = new St.Bin({ x_fill: true, | ||||
|                                      height: size * scale_factor, | ||||
|                                      width: size * scale_factor }); | ||||
|  | ||||
|         this.actor.add_actor(this._iconBin); | ||||
|         this.actor.add_actor(this._counterBin); | ||||
|  | ||||
|         this._source.connect('count-updated', Lang.bind(this, this._updateCount)); | ||||
|         this._updateCount(); | ||||
|  | ||||
|         this._source.connect('icon-updated', Lang.bind(this, this._updateIcon)); | ||||
|         this._updateIcon(); | ||||
| @@ -1208,6 +1191,48 @@ const SourceActor = new Lang.Class({ | ||||
|     _allocate: function(actor, box, flags) { | ||||
|         // the iconBin should fill our entire box | ||||
|         this._iconBin.allocate(box, flags); | ||||
|     }, | ||||
|  | ||||
|     _updateIcon: function() { | ||||
|         if (this._actorDestroyed) | ||||
|             return; | ||||
|  | ||||
|         if (!this._iconSet) | ||||
|             this._iconBin.child = this._source.createIcon(this._size); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const SourceActorWithLabel = new Lang.Class({ | ||||
|     Name: 'SourceActorWithLabel', | ||||
|     Extends: SourceActor, | ||||
|  | ||||
|     _init: function(source, size) { | ||||
|         this.parent(source, size); | ||||
|  | ||||
|         this._counterLabel = new St.Label({ x_align: Clutter.ActorAlign.CENTER, | ||||
|                                             x_expand: true, | ||||
|                                             y_align: Clutter.ActorAlign.CENTER, | ||||
|                                             y_expand: true }); | ||||
|  | ||||
|         this._counterBin = new St.Bin({ style_class: 'summary-source-counter', | ||||
|                                         child: this._counterLabel, | ||||
|                                         layout_manager: new Clutter.BinLayout() }); | ||||
|         this._counterBin.hide(); | ||||
|  | ||||
|         this._counterBin.connect('style-changed', Lang.bind(this, function() { | ||||
|             let themeNode = this._counterBin.get_theme_node(); | ||||
|             this._counterBin.translation_x = themeNode.get_length('-shell-counter-overlap-x'); | ||||
|             this._counterBin.translation_y = themeNode.get_length('-shell-counter-overlap-y'); | ||||
|         })); | ||||
|  | ||||
|         this.actor.add_actor(this._counterBin); | ||||
|  | ||||
|         this._source.connect('count-updated', Lang.bind(this, this._updateCount)); | ||||
|         this._updateCount(); | ||||
|     }, | ||||
|  | ||||
|     _allocate: function(actor, box, flags) { | ||||
|         this.parent(actor, box, flags); | ||||
|  | ||||
|         let childBox = new Clutter.ActorBox(); | ||||
|  | ||||
| @@ -1230,14 +1255,6 @@ const SourceActor = new Lang.Class({ | ||||
|         this._counterBin.allocate(childBox, flags); | ||||
|     }, | ||||
|  | ||||
|     _updateIcon: function() { | ||||
|         if (this._actorDestroyed) | ||||
|             return; | ||||
|  | ||||
|         if (!this._iconSet) | ||||
|             this._iconBin.child = this._source.createIcon(this._size); | ||||
|     }, | ||||
|  | ||||
|     _updateCount: function() { | ||||
|         if (this._actorDestroyed) | ||||
|             return; | ||||
| @@ -1263,7 +1280,6 @@ const Source = new Lang.Class({ | ||||
|         this.title = title; | ||||
|         this.iconName = iconName; | ||||
|  | ||||
|         this.isTransient = false; | ||||
|         this.isChat = false; | ||||
|         this.isMuted = false; | ||||
|         this.keepTrayOnSummaryClick = false; | ||||
| @@ -1323,10 +1339,6 @@ const Source = new Lang.Class({ | ||||
|         return rightClickMenu; | ||||
|     }, | ||||
|  | ||||
|     setTransient: function(isTransient) { | ||||
|         this.isTransient = isTransient; | ||||
|     }, | ||||
|  | ||||
|     setTitle: function(newTitle) { | ||||
|         this.title = newTitle; | ||||
|         this.emit('title-changed'); | ||||
| @@ -1355,7 +1367,7 @@ const Source = new Lang.Class({ | ||||
|         if (this._mainIcon) | ||||
|             return; | ||||
|  | ||||
|         this._mainIcon = new SourceActor(this, this.SOURCE_ICON_SIZE); | ||||
|         this._mainIcon = new SourceActorWithLabel(this, this.SOURCE_ICON_SIZE); | ||||
|     }, | ||||
|  | ||||
|     // Unlike createIcon, this always returns the same actor; | ||||
| @@ -1528,9 +1540,9 @@ const SummaryItem = new Lang.Class({ | ||||
|     _onKeyPress: function(actor, event) { | ||||
|         if (event.get_key_symbol() == Clutter.KEY_Up) { | ||||
|             actor.emit('clicked', 1); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     prepareNotificationStackForShowing: function() { | ||||
| @@ -1625,7 +1637,7 @@ const MessageTrayMenu = new Lang.Class({ | ||||
|         this._clearItem = this.addAction(_("Clear Messages"), function() { | ||||
|             let toDestroy = tray.getSources().filter(function(source) { | ||||
|                 return source.isClearable; | ||||
|             }) | ||||
|             }); | ||||
|  | ||||
|             toDestroy.forEach(function(source) { | ||||
|                 source.destroy(); | ||||
| @@ -1788,6 +1800,7 @@ const MessageTray = new Lang.Class({ | ||||
|             this._setClickedSummaryItem(null); | ||||
|             this._updateState(); | ||||
|             actor.grab_key_focus(); | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|         global.focus_manager.add_group(this.actor); | ||||
|         this._summary = new St.BoxLayout({ style_class: 'message-tray-summary', | ||||
| @@ -1902,7 +1915,7 @@ const MessageTray = new Lang.Class({ | ||||
|                               Shell.KeyBindingMode.OVERVIEW, | ||||
|                               Lang.bind(this, this._expandActiveNotification)); | ||||
|  | ||||
|         this._sources = new Hash.Map(); | ||||
|         this._sources = new Map(); | ||||
|         this._chatSummaryItemsCount = 0; | ||||
|  | ||||
|         this._trayDwellTimeoutId = 0; | ||||
| @@ -1939,7 +1952,7 @@ const MessageTray = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _updateNoMessagesLabel: function() { | ||||
|         this._noMessages.visible = this._sources.size() == 0; | ||||
|         this._noMessages.visible = this._sources.size == 0; | ||||
|     }, | ||||
|  | ||||
|     _sessionUpdated: function() { | ||||
| @@ -1993,32 +2006,37 @@ const MessageTray = new Lang.Class({ | ||||
|         this._trayDwellTimeoutId = 0; | ||||
|  | ||||
|         if (Main.layoutManager.bottomMonitor.inFullscreen) | ||||
|             return false; | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|  | ||||
|         // We don't want to open the tray when a modal dialog | ||||
|         // is up, so we check the modal count for that. When we are in the | ||||
|         // overview we have to take the overview's modal push into account | ||||
|         if (Main.modalCount > (Main.overview.visible ? 1 : 0)) | ||||
|             return false; | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|  | ||||
|         // If the user interacted with the focus window since we started the tray | ||||
|         // dwell (by clicking or typing), don't activate the message tray | ||||
|         let focusWindow = global.display.focus_window; | ||||
|         let currentUserTime = focusWindow ? focusWindow.user_time : 0; | ||||
|         if (currentUserTime != this._trayDwellUserTime) | ||||
|             return false; | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|  | ||||
|         this.openTray(); | ||||
|         return false; | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     _onNotificationKeyRelease: function(actor, event) { | ||||
|         if (event.get_key_symbol() == Clutter.KEY_Escape && event.get_state() == 0) { | ||||
|             this._closeNotification(); | ||||
|             return true; | ||||
|             this._expireNotification(); | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _expireNotification: function() { | ||||
|         this._notificationExpired = true; | ||||
|         this._updateState(); | ||||
|     }, | ||||
|  | ||||
|     _closeNotification: function() { | ||||
| @@ -2102,7 +2120,8 @@ const MessageTray = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _removeSource: function(source) { | ||||
|         let [, obj] = this._sources.delete(source); | ||||
|         let obj = this._sources.get(source); | ||||
|         this._sources.delete(source); | ||||
|         let summaryItem = obj.summaryItem; | ||||
|  | ||||
|         if (source.isChat) | ||||
| @@ -2123,7 +2142,7 @@ const MessageTray = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     getSources: function() { | ||||
|         return this._sources.keys(); | ||||
|         return [k for (k of this._sources.keys())]; | ||||
|     }, | ||||
|  | ||||
|     _onSourceEnableChanged: function(policy, source) { | ||||
| @@ -2339,7 +2358,7 @@ const MessageTray = new Lang.Class({ | ||||
|             this._updateNotificationTimeout(0); | ||||
|             this._updateState(); | ||||
|         } | ||||
|         return false; | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     _escapeTray: function() { | ||||
| @@ -2356,6 +2375,13 @@ const MessageTray = new Lang.Class({ | ||||
|     // _updateState() figures out what (if anything) needs to be done | ||||
|     // at the present time. | ||||
|     _updateState: function() { | ||||
|         // If our state changes caused _updateState to be called, | ||||
|         // just exit now to prevent reentrancy issues. | ||||
|         if (this._updatingState) | ||||
|             return; | ||||
|  | ||||
|         this._updatingState = true; | ||||
|  | ||||
|         // Filter out acknowledged notifications. | ||||
|         this._notificationQueue = this._notificationQueue.filter(function(n) { | ||||
|             return !n.acknowledged; | ||||
| @@ -2375,9 +2401,9 @@ const MessageTray = new Lang.Class({ | ||||
|         } else if (this._notificationState == State.SHOWN) { | ||||
|             let expired = (this._userActiveWhileNotificationShown && | ||||
|                            this._notificationTimeoutId == 0 && | ||||
|                            !(this._notification.urgency == Urgency.CRITICAL) && | ||||
|                            this._notification.urgency != Urgency.CRITICAL && | ||||
|                            !this._notification.focused && | ||||
|                            !this._pointerInNotification); | ||||
|                            !this._pointerInNotification) || this._notificationExpired; | ||||
|             let mustClose = (this._notificationRemoved || !hasNotifications || expired || this._traySummoned); | ||||
|  | ||||
|             if (mustClose) { | ||||
| @@ -2432,11 +2458,16 @@ const MessageTray = new Lang.Class({ | ||||
|                                      this._desktopCloneState == State.SHOWN); | ||||
|         let desktopCloneShouldBeVisible = (trayShouldBeVisible); | ||||
|  | ||||
|         if (!desktopCloneIsVisible && desktopCloneShouldBeVisible) { | ||||
|         if (!desktopCloneIsVisible && desktopCloneShouldBeVisible) | ||||
|             this._showDesktopClone(); | ||||
|         } else if (desktopCloneIsVisible && !desktopCloneShouldBeVisible) { | ||||
|         else if (desktopCloneIsVisible && !desktopCloneShouldBeVisible) | ||||
|             this._hideDesktopClone(); | ||||
|         } | ||||
|  | ||||
|         this._updatingState = false; | ||||
|  | ||||
|         // Clean transient variables that are used to communicate actions | ||||
|         // to updateState() | ||||
|         this._notificationExpired = false; | ||||
|     }, | ||||
|  | ||||
|     _tween: function(actor, statevar, value, params) { | ||||
| @@ -2664,7 +2695,7 @@ const MessageTray = new Lang.Class({ | ||||
|  | ||||
|         this._lastSeenMouseX = x; | ||||
|         this._lastSeenMouseY = y; | ||||
|         return false; | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     _hideNotification: function(animate) { | ||||
| @@ -2759,7 +2790,12 @@ const MessageTray = new Lang.Class({ | ||||
|                              { y: expandedY, | ||||
|                                opacity: 255, | ||||
|                                time: ANIMATION_TIME, | ||||
|                                transition: 'easeOutQuad' | ||||
|                                transition: 'easeOutQuad', | ||||
|                                // HACK: Drive the state machine here better, | ||||
|                                // instead of overwriting tweens | ||||
|                                onComplete: Lang.bind(this, function() { | ||||
|                                    this._notificationState = State.SHOWN; | ||||
|                                }), | ||||
|                              }); | ||||
|         } | ||||
|     }, | ||||
| @@ -2801,13 +2837,13 @@ const MessageTray = new Lang.Class({ | ||||
|                                                                                   Lang.bind(this, this._onSourceDoneDisplayingContent)); | ||||
|  | ||||
|         this._summaryBoxPointer.bin.child = child; | ||||
|         this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child, | ||||
|                                 onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) }); | ||||
|  | ||||
|         this._summaryBoxPointer.actor.opacity = 0; | ||||
|         this._summaryBoxPointer.actor.show(); | ||||
|         this._adjustSummaryBoxPointerPosition(); | ||||
|  | ||||
|         this._grabHelper.grab({ actor: this._summaryBoxPointer.bin.child, | ||||
|                                 onUngrab: Lang.bind(this, this._onSummaryBoxPointerUngrabbed) }); | ||||
|  | ||||
|         this._summaryBoxPointerState = State.SHOWING; | ||||
|         this._summaryBoxPointer.show(BoxPointer.PopupAnimation.FULL, Lang.bind(this, function() { | ||||
|             this._summaryBoxPointerState = State.SHOWN; | ||||
| @@ -2862,13 +2898,13 @@ const MessageTray = new Lang.Class({ | ||||
|         case Clutter.KEY_Escape: | ||||
|             this._setClickedSummaryItem(null); | ||||
|             this._updateState(); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         case Clutter.KEY_Delete: | ||||
|             this._clickedSummaryItem.source.destroy(); | ||||
|             this._escapeTray(); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onSummaryBoxPointerUngrabbed: function() { | ||||
| @@ -2906,8 +2942,6 @@ const MessageTray = new Lang.Class({ | ||||
|             this._summaryBoxPointerItem.doneShowingNotificationStack(); | ||||
|             this._summaryBoxPointerItem = null; | ||||
|  | ||||
|             if (source.isTransient && !this._reNotifyAfterHideNotification) | ||||
|                 source.destroy(NotificationDestroyedReason.EXPIRED); | ||||
|             if (this._reNotifyAfterHideNotification) { | ||||
|                 this._onNotify(this._reNotifyAfterHideNotification.source, this._reNotifyAfterHideNotification); | ||||
|                 this._reNotifyAfterHideNotification = null; | ||||
| @@ -2926,7 +2960,6 @@ const SystemNotificationSource = new Lang.Class({ | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent(_("System Information"), 'dialog-information-symbolic'); | ||||
|         this.setTransient(true); | ||||
|     }, | ||||
|  | ||||
|     open: function() { | ||||
|   | ||||
| @@ -41,7 +41,6 @@ const ModalDialog = new Lang.Class({ | ||||
|     _init: function(params) { | ||||
|         params = Params.parse(params, { shellReactive: false, | ||||
|                                         styleClass: null, | ||||
|                                         parentActor: Main.uiGroup, | ||||
|                                         keybindingMode: Shell.KeyBindingMode.SYSTEM_MODAL, | ||||
|                                         shouldFadeIn: true, | ||||
|                                         destroyOnClose: true }); | ||||
| @@ -57,7 +56,7 @@ const ModalDialog = new Lang.Class({ | ||||
|                                       x: 0, | ||||
|                                       y: 0, | ||||
|                                       accessible_role: Atk.Role.DIALOG }); | ||||
|         params.parentActor.add_actor(this._group); | ||||
|         Main.layoutManager.modalDialogGroup.add_actor(this._group); | ||||
|  | ||||
|         let constraint = new Clutter.BindConstraint({ source: global.stage, | ||||
|                                                       coordinate: Clutter.BindCoordinate.ALL }); | ||||
| @@ -89,7 +88,8 @@ const ModalDialog = new Lang.Class({ | ||||
|  | ||||
|         if (!this._shellReactive) { | ||||
|             this._lightbox = new Lightbox.Lightbox(this._group, | ||||
|                                                    { inhibitEvents: true }); | ||||
|                                                    { inhibitEvents: true, | ||||
|                                                      radialEffect: true }); | ||||
|             this._lightbox.highlight(this._backgroundBin); | ||||
|  | ||||
|             this._eventBlocker = new Clutter.Actor({ reactive: true }); | ||||
| @@ -229,6 +229,7 @@ const ModalDialog = new Lang.Class({ | ||||
|  | ||||
|     _onKeyPressEvent: function(object, event) { | ||||
|         this._pressedKey = event.get_key_symbol(); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onKeyReleaseEvent: function(object, event) { | ||||
| @@ -237,21 +238,21 @@ const ModalDialog = new Lang.Class({ | ||||
|  | ||||
|         let symbol = event.get_key_symbol(); | ||||
|         if (symbol != pressedKey) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let buttonInfo = this._buttonKeys[symbol]; | ||||
|         if (!buttonInfo) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let button = buttonInfo['button']; | ||||
|         let action = buttonInfo['action']; | ||||
|  | ||||
|         if (action && button.reactive) { | ||||
|             action(); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onGroupDestroy: function() { | ||||
|   | ||||
| @@ -181,12 +181,10 @@ const FdoNotificationDaemon = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     // Returns the source associated with ndata.notification if it is set. | ||||
|     // Otherwise, returns the source associated with the title and pid if | ||||
|     // such source is stored in this._sources and the notification is not | ||||
|     // transient. If the existing or requested source is associated with | ||||
|     // a tray icon and passed in pid matches a pid of an existing source, | ||||
|     // the title match is ignored to enable representing a tray icon and | ||||
|     // notifications from the same application with a single source. | ||||
|     // If the existing or requested source is associated with a tray icon | ||||
|     // and passed in pid matches a pid of an existing source, the title | ||||
|     // match is ignored to enable representing a tray icon and notifications | ||||
|     // from the same application with a single source. | ||||
|     // | ||||
|     // If no existing source is found, a new source is created as long as | ||||
|     // pid is provided. | ||||
| @@ -204,32 +202,20 @@ const FdoNotificationDaemon = new Lang.Class({ | ||||
|         if (ndata && ndata.notification) | ||||
|             return ndata.notification.source; | ||||
|  | ||||
|         let isForTransientNotification = (ndata && ndata.hints['transient'] == true); | ||||
|  | ||||
|         // We don't want to override a persistent notification | ||||
|         // with a transient one from the same sender, so we | ||||
|         // always create a new source object for new transient notifications | ||||
|         // and never add it to this._sources . | ||||
|         if (!isForTransientNotification) { | ||||
|             let source = this._lookupSource(title, pid, trayIcon); | ||||
|             if (source) { | ||||
|                 source.setTitle(title); | ||||
|                 return source; | ||||
|             } | ||||
|         let source = this._lookupSource(title, pid, trayIcon); | ||||
|         if (source) { | ||||
|             source.setTitle(title); | ||||
|             return source; | ||||
|         } | ||||
|  | ||||
|         let source = new FdoNotificationDaemonSource(title, pid, sender, trayIcon, ndata ? ndata.hints['desktop-entry'] : null); | ||||
|         source.setTransient(isForTransientNotification); | ||||
|  | ||||
|         if (!isForTransientNotification) { | ||||
|             this._sources.push(source); | ||||
|             source.connect('destroy', Lang.bind(this, | ||||
|                 function() { | ||||
|                     let index = this._sources.indexOf(source); | ||||
|                     if (index >= 0) | ||||
|                         this._sources.splice(index, 1); | ||||
|                 })); | ||||
|         } | ||||
|         this._sources.push(source); | ||||
|         source.connect('destroy', Lang.bind(this, function() { | ||||
|             let index = this._sources.indexOf(source); | ||||
|             if (index >= 0) | ||||
|                 this._sources.splice(index, 1); | ||||
|         })); | ||||
|  | ||||
|         Main.messageTray.add(source); | ||||
|         return source; | ||||
| @@ -261,7 +247,7 @@ const FdoNotificationDaemon = new Lang.Class({ | ||||
|             Mainloop.idle_add(Lang.bind(this, | ||||
|                                         function () { | ||||
|                                             this._emitNotificationClosed(id, NotificationClosedReason.DISMISSED); | ||||
|                                             return false; | ||||
|                                             return GLib.SOURCE_REMOVE; | ||||
|                                         })); | ||||
|             return invocation.return_value(GLib.Variant.new('(u)', [id])); | ||||
|         } | ||||
| @@ -336,20 +322,10 @@ const FdoNotificationDaemon = new Lang.Class({ | ||||
|             let [pid] = result; | ||||
|             source = this._getSource(appName, pid, ndata, sender, null); | ||||
|  | ||||
|             // We only store sender-pid entries for persistent sources. | ||||
|             // Removing the entries once the source is destroyed | ||||
|             // would result in the entries associated with transient | ||||
|             // sources removed once the notification is shown anyway. | ||||
|             // However, keeping these pairs would mean that we would | ||||
|             // possibly remove an entry associated with a persistent | ||||
|             // source when a transient source for the same sender is | ||||
|             // distroyed. | ||||
|             if (!source.isTransient) { | ||||
|                 this._senderToPid[sender] = pid; | ||||
|                 source.connect('destroy', Lang.bind(this, function() { | ||||
|                     delete this._senderToPid[sender]; | ||||
|                 })); | ||||
|             } | ||||
|             this._senderToPid[sender] = pid; | ||||
|             source.connect('destroy', Lang.bind(this, function() { | ||||
|                 delete this._senderToPid[sender]; | ||||
|             })); | ||||
|             this._notifyForSource(source, ndata); | ||||
|         })); | ||||
|  | ||||
| @@ -715,6 +691,12 @@ const FdoNotificationDaemonSource = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const PRIORITY_URGENCY_MAP = { | ||||
|     low: MessageTray.Urgency.LOW, | ||||
|     normal: MessageTray.Urgency.NORMAL, | ||||
|     high: MessageTray.Urgency.HIGH, | ||||
|     urgent: MessageTray.Urgency.CRITICAL | ||||
| }; | ||||
|  | ||||
| const GtkNotificationDaemonNotification = new Lang.Class({ | ||||
|     Name: 'GtkNotificationDaemonNotification', | ||||
| @@ -728,12 +710,20 @@ const GtkNotificationDaemonNotification = new Lang.Class({ | ||||
|               "body": body, | ||||
|               "icon": gicon, | ||||
|               "urgent": urgent, | ||||
|               "priority": priority, | ||||
|               "buttons": buttons, | ||||
|               "default-action": defaultAction, | ||||
|               "default-action-target": defaultActionTarget } = notification; | ||||
|  | ||||
|         this.setUrgency(urgent.unpack() ? MessageTray.Urgency.CRITICAL | ||||
|                                         : MessageTray.Urgency.NORMAL); | ||||
|         if (priority) { | ||||
|             let urgency = PRIORITY_URGENCY_MAP[priority.unpack()]; | ||||
|             this.setUrgency(urgency != undefined ? urgency : MessageTray.Urgency.NORMAL); | ||||
|         } else if (urgent) { | ||||
|             this.setUrgency(urgent.unpack() ? MessageTray.Urgency.CRITICAL | ||||
|                             : MessageTray.Urgency.NORMAL); | ||||
|         } else { | ||||
|             this.setUrgency(MessageTray.Urgency.NORMAL); | ||||
|         } | ||||
|  | ||||
|         if (buttons) { | ||||
|             buttons.deep_unpack().forEach(Lang.bind(this, function(button) { | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GLib = imports.gi.GLib; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const Lang = imports.lang; | ||||
| @@ -65,6 +66,7 @@ const LevelBar = new Lang.Class({ | ||||
|         cr.arc(radius, h - radius, radius, 0.5 * Math.PI, Math.PI); | ||||
|         cr.arc(radius, radius, radius, Math.PI, 1.5 * Math.PI); | ||||
|         cr.fill(); | ||||
|         cr.$dispose(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -77,7 +79,8 @@ const OsdWindow = new Lang.Class({ | ||||
|                                      y_expand: true, | ||||
|                                      x_align: Clutter.ActorAlign.CENTER, | ||||
|                                      y_align: Clutter.ActorAlign.CENTER }); | ||||
|         this.actor.add_constraint(new Layout.MonitorConstraint({ primary: true })); | ||||
|         this._currentMonitor = undefined; | ||||
|         this.setMonitor (-1); | ||||
|         this._box = new St.BoxLayout({ style_class: 'osd-window', | ||||
|                                        vertical: true }); | ||||
|         this.actor.add_actor(this._box); | ||||
| @@ -122,13 +125,15 @@ const OsdWindow = new Lang.Class({ | ||||
|  | ||||
|     setLevel: function(level) { | ||||
|         this._level.actor.visible = (level != undefined); | ||||
|         if (this.actor.visible) | ||||
|             Tweener.addTween(this._level, | ||||
|                              { level: level, | ||||
|                                time: LEVEL_ANIMATION_TIME, | ||||
|                                transition: 'easeOutQuad' }); | ||||
|         else | ||||
|             this._level.level = level; | ||||
|         if (level != undefined) { | ||||
|             if (this.actor.visible) | ||||
|                 Tweener.addTween(this._level, | ||||
|                                  { level: level, | ||||
|                                    time: LEVEL_ANIMATION_TIME, | ||||
|                                    transition: 'easeOutQuad' }); | ||||
|             else | ||||
|                 this._level.level = level; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     show: function() { | ||||
| @@ -172,6 +177,7 @@ const OsdWindow = new Lang.Class({ | ||||
|                               Meta.enable_unredirect_for_screen(global.screen); | ||||
|                            }) | ||||
|                          }); | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     _reset: function() { | ||||
| @@ -182,14 +188,21 @@ const OsdWindow = new Lang.Class({ | ||||
|  | ||||
|     _monitorsChanged: function() { | ||||
|         /* assume 110x110 on a 640x480 display and scale from there */ | ||||
|         let monitor = Main.layoutManager.primaryMonitor; | ||||
|         let monitor; | ||||
|  | ||||
|         if (this._currentMonitor >= 0) | ||||
|             monitor = Main.layoutManager.monitors[this._currentMonitor]; | ||||
|         else | ||||
|             monitor = Main.layoutManager.primaryMonitor; | ||||
|  | ||||
|         let scalew = monitor.width / 640.0; | ||||
|         let scaleh = monitor.height / 480.0; | ||||
|         let scale = Math.min(scalew, scaleh); | ||||
|         this._popupSize = 110 * Math.max(1, scale); | ||||
|  | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|         this._icon.icon_size = this._popupSize / (2 * scaleFactor); | ||||
|         this._box.translation_y = monitor.height / 4; | ||||
|         this._icon.icon_size = this._popupSize / 2; | ||||
|         this._box.style_changed(); | ||||
|     }, | ||||
|  | ||||
| @@ -205,6 +218,27 @@ const OsdWindow = new Lang.Class({ | ||||
|         let minWidth = this._popupSize - verticalPadding - leftBorder - rightBorder; | ||||
|         let minHeight = this._popupSize - horizontalPadding - topBorder - bottomBorder; | ||||
|  | ||||
|         this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight)); | ||||
|         // minWidth/minHeight here are in real pixels, | ||||
|         // but the theme takes measures in unscaled dimensions | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|         this._box.style = 'min-height: %dpx;'.format(Math.max(minWidth, minHeight) / scaleFactor); | ||||
|     }, | ||||
|  | ||||
|     setMonitor: function(index) { | ||||
|         let constraint; | ||||
|  | ||||
|         if (index < 0) | ||||
|             index = -1; | ||||
|         if (this._currentMonitor == index) | ||||
|             return; | ||||
|  | ||||
|         if (index < 0) | ||||
|             constraint = new Layout.MonitorConstraint({ primary: true }); | ||||
|         else | ||||
|             constraint = new Layout.MonitorConstraint({ index: index }); | ||||
|  | ||||
|         this.actor.clear_constraints(); | ||||
|         this.actor.add_constraint(constraint); | ||||
|         this._currentMonitor = index; | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Mainloop = imports.mainloop; | ||||
| @@ -13,6 +14,7 @@ const Gdk = imports.gi.Gdk; | ||||
| const Background = imports.ui.background; | ||||
| const DND = imports.ui.dnd; | ||||
| const LayoutManager = imports.ui.layout; | ||||
| const Lightbox = imports.ui.lightbox; | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const OverviewControls = imports.ui.overviewControls; | ||||
| @@ -112,9 +114,6 @@ const Overview = new Lang.Class({ | ||||
|         // rendering options without duplicating the texture data. | ||||
|         let monitor = Main.layoutManager.primaryMonitor; | ||||
|  | ||||
|         this._desktopFade = new St.Bin(); | ||||
|         Main.layoutManager.overviewGroup.add_child(this._desktopFade); | ||||
|  | ||||
|         let layout = new Clutter.BinLayout(); | ||||
|         this._stack = new Clutter.Actor({ layout_manager: layout }); | ||||
|         this._stack.add_constraint(new LayoutManager.MonitorConstraint({ primary: true })); | ||||
| @@ -133,6 +132,9 @@ const Overview = new Lang.Class({ | ||||
|         Main.layoutManager.overviewGroup.add_child(this._backgroundGroup); | ||||
|         this._bgManagers = []; | ||||
|  | ||||
|         this._desktopFade = new St.Widget(); | ||||
|         Main.layoutManager.overviewGroup.add_child(this._desktopFade); | ||||
|  | ||||
|         this._activationTime = 0; | ||||
|  | ||||
|         this.visible = false;           // animating to overview, in overview, animating out | ||||
| @@ -147,7 +149,7 @@ const Overview = new Lang.Class({ | ||||
|         this._coverPane = new Clutter.Actor({ opacity: 0, | ||||
|                                               reactive: true }); | ||||
|         Main.layoutManager.overviewGroup.add_child(this._coverPane); | ||||
|         this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return true; })); | ||||
|         this._coverPane.connect('event', Lang.bind(this, function (actor, event) { return Clutter.EVENT_STOP; })); | ||||
|  | ||||
|         this._stack.add_actor(this._overview); | ||||
|         Main.layoutManager.overviewGroup.add_child(this._stack); | ||||
| @@ -195,11 +197,7 @@ const Overview = new Lang.Class({ | ||||
|  | ||||
|             Tweener.addTween(background, | ||||
|                              { brightness: 1.0, | ||||
|                                time: SHADE_ANIMATION_TIME, | ||||
|                                transition: 'easeOutQuad' | ||||
|                              }); | ||||
|             Tweener.addTween(background, | ||||
|                              { vignetteSharpness: 0.0, | ||||
|                                vignetteSharpness: 0.0, | ||||
|                                time: SHADE_ANIMATION_TIME, | ||||
|                                transition: 'easeOutQuad' | ||||
|                              }); | ||||
| @@ -212,12 +210,8 @@ const Overview = new Lang.Class({ | ||||
|             let background = backgrounds[i]._delegate; | ||||
|  | ||||
|             Tweener.addTween(background, | ||||
|                              { brightness: 0.8, | ||||
|                                time: SHADE_ANIMATION_TIME, | ||||
|                                transition: 'easeOutQuad' | ||||
|                              }); | ||||
|             Tweener.addTween(background, | ||||
|                              { vignetteSharpness: 0.7, | ||||
|                              { brightness: Lightbox.VIGNETTE_BRIGHTNESS, | ||||
|                                vignetteSharpness: Lightbox.VIGNETTE_SHARPNESS, | ||||
|                                time: SHADE_ANIMATION_TIME, | ||||
|                                transition: 'easeOutQuad' | ||||
|                              }); | ||||
| @@ -369,7 +363,7 @@ const Overview = new Lang.Class({ | ||||
|                                                                     this._windowSwitchTimestamp); | ||||
|                                                 this.hide(); | ||||
|                                                 this._lastHoveredWindow = null; | ||||
|                                                 return false; | ||||
|                                                 return GLib.SOURCE_REMOVE; | ||||
|                                             })); | ||||
|         } | ||||
|  | ||||
| @@ -378,6 +372,7 @@ const Overview = new Lang.Class({ | ||||
|  | ||||
|     _onScrollEvent: function(actor, event) { | ||||
|         this.emit('scroll-event', event); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     addAction: function(action) { | ||||
| @@ -491,8 +486,13 @@ const Overview = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     fadeOutDesktop: function() { | ||||
|         if (!this._desktopFade.child) | ||||
|             this._desktopFade.child = this._getDesktopClone(); | ||||
|         if (!this._desktopFade.get_n_children()) { | ||||
|             let clone = this._getDesktopClone(); | ||||
|             if (!clone) | ||||
|                 return; | ||||
|  | ||||
|             this._desktopFade.add_child(clone); | ||||
|         } | ||||
|  | ||||
|         this._desktopFade.opacity = 255; | ||||
|         this._desktopFade.show(); | ||||
|   | ||||
| @@ -460,9 +460,6 @@ const MessagesIndicator = new Lang.Class({ | ||||
|         if (source.trayIcon) | ||||
|             return; | ||||
|  | ||||
|         if (source.isTransient) | ||||
|             return; | ||||
|  | ||||
|         source.connect('count-updated', Lang.bind(this, this._updateCount)); | ||||
|         this._sources.push(source); | ||||
|         this._updateCount(); | ||||
|   | ||||
| @@ -213,7 +213,7 @@ const AppMenuButton = new Lang.Class({ | ||||
|         this._label = new TextShadower(); | ||||
|         this._label.actor.y_align = Clutter.ActorAlign.CENTER; | ||||
|         this._hbox.add_actor(this._label.actor); | ||||
|         this._arrow = PopupMenu.unicodeArrow(St.Side.BOTTOM); | ||||
|         this._arrow = PopupMenu.arrowIcon(St.Side.BOTTOM); | ||||
|         this._hbox.add_actor(this._arrow); | ||||
|  | ||||
|         this._iconBottomClip = 0; | ||||
| @@ -602,14 +602,15 @@ const ActivitiesButton = new Lang.Class({ | ||||
|     _onCapturedEvent: function(actor, event) { | ||||
|         if (event.type() == Clutter.EventType.BUTTON_PRESS) { | ||||
|             if (!Main.overview.shouldToggleByCornerOrButton()) | ||||
|                 return true; | ||||
|                 return Clutter.EVENT_STOP; | ||||
|         } | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onButtonRelease: function() { | ||||
|         Main.overview.toggle(); | ||||
|         this.menu.close(); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onKeyRelease: function(actor, event) { | ||||
| @@ -617,6 +618,7 @@ const ActivitiesButton = new Lang.Class({ | ||||
|         if (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_space) { | ||||
|             Main.overview.toggle(); | ||||
|         } | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _xdndToggleOverview: function(actor) { | ||||
| @@ -628,6 +630,7 @@ const ActivitiesButton = new Lang.Class({ | ||||
|  | ||||
|         Mainloop.source_remove(this._xdndTimeOut); | ||||
|         this._xdndTimeOut = 0; | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -809,7 +812,11 @@ const AggregateMenu = new Lang.Class({ | ||||
|         this._indicators = new St.BoxLayout({ style_class: 'panel-status-indicators-box' }); | ||||
|         this.actor.add_child(this._indicators); | ||||
|  | ||||
|         this._network = new imports.ui.status.network.NMApplet(); | ||||
|         if (Config.HAVE_NETWORKMANAGER) { | ||||
|             this._network = new imports.ui.status.network.NMApplet(); | ||||
|         } else { | ||||
|             this._network = null; | ||||
|         } | ||||
|         if (Config.HAVE_BLUETOOTH) { | ||||
|             this._bluetooth = new imports.ui.status.bluetooth.Indicator(); | ||||
|         } else { | ||||
| @@ -822,24 +829,31 @@ const AggregateMenu = new Lang.Class({ | ||||
|         this._brightness = new imports.ui.status.brightness.Indicator(); | ||||
|         this._system = new imports.ui.status.system.Indicator(); | ||||
|         this._screencast = new imports.ui.status.screencast.Indicator(); | ||||
|         this._location = new imports.ui.status.location.Indicator(); | ||||
|  | ||||
|         this._indicators.add_child(this._screencast.indicators); | ||||
|         this._indicators.add_child(this._network.indicators); | ||||
|         this._indicators.add_child(this._location.indicators); | ||||
|         if (this._network) { | ||||
|             this._indicators.add_child(this._network.indicators); | ||||
|         } | ||||
|         if (this._bluetooth) { | ||||
|             this._indicators.add_child(this._bluetooth.indicators); | ||||
|         } | ||||
|         this._indicators.add_child(this._rfkill.indicators); | ||||
|         this._indicators.add_child(this._volume.indicators); | ||||
|         this._indicators.add_child(this._power.indicators); | ||||
|         this._indicators.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM)); | ||||
|         this._indicators.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM)); | ||||
|  | ||||
|         this.menu.addMenuItem(this._volume.menu); | ||||
|         this.menu.addMenuItem(this._brightness.menu); | ||||
|         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
|         this.menu.addMenuItem(this._network.menu); | ||||
|         if (this._network) { | ||||
|             this.menu.addMenuItem(this._network.menu); | ||||
|         } | ||||
|         if (this._bluetooth) { | ||||
|             this.menu.addMenuItem(this._bluetooth.menu); | ||||
|         } | ||||
|         this.menu.addMenuItem(this._location.menu); | ||||
|         this.menu.addMenuItem(this._rfkill.menu); | ||||
|         this.menu.addMenuItem(this._power.menu); | ||||
|         this.menu.addMenuItem(new PopupMenu.PopupSeparatorMenuItem()); | ||||
| @@ -983,23 +997,23 @@ const Panel = new Lang.Class({ | ||||
|  | ||||
|     _onButtonPress: function(actor, event) { | ||||
|         if (Main.modalCount > 0) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (event.get_source() != actor) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let button = event.get_button(); | ||||
|         if (button != 1) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let focusWindow = global.display.focus_window; | ||||
|         if (!focusWindow) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let dragWindow = focusWindow.is_attached_dialog() ? focusWindow.get_transient_for() | ||||
|                                                           : focusWindow; | ||||
|         if (!dragWindow) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let rect = dragWindow.get_outer_rect(); | ||||
|         let [stageX, stageY] = event.get_coords(); | ||||
| @@ -1008,7 +1022,7 @@ const Panel = new Lang.Class({ | ||||
|                         stageX > rect.x && stageX < rect.x + rect.width; | ||||
|  | ||||
|         if (!allowDrag) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         global.display.begin_grab_op(global.screen, | ||||
|                                      dragWindow, | ||||
| @@ -1020,7 +1034,7 @@ const Panel = new Lang.Class({ | ||||
|                                      event.get_time(), | ||||
|                                      stageX, stageY); | ||||
|  | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     toggleAppMenu: function() { | ||||
|   | ||||
| @@ -137,29 +137,30 @@ const Button = new Lang.Class({ | ||||
|  | ||||
|     _onButtonPress: function(actor, event) { | ||||
|         if (!this.menu) | ||||
|             return; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         this.menu.toggle(); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onSourceKeyPress: function(actor, event) { | ||||
|         if (!this.menu) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let symbol = event.get_key_symbol(); | ||||
|         if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) { | ||||
|             this.menu.toggle(); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } else if (symbol == Clutter.KEY_Escape && this.menu.isOpen) { | ||||
|             this.menu.close(); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } else if (symbol == Clutter.KEY_Down) { | ||||
|             if (!this.menu.isOpen) | ||||
|                 this.menu.toggle(); | ||||
|             this.menu.actor.navigate_focus(this.actor, Gtk.DirectionType.DOWN, false); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } else | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onVisibilityChanged: function() { | ||||
| @@ -172,7 +173,7 @@ const Button = new Lang.Class({ | ||||
|  | ||||
|     _onMenuKeyPress: function(actor, event) { | ||||
|         if (global.focus_manager.navigate_from_event(event)) | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|  | ||||
|         let symbol = event.get_key_symbol(); | ||||
|         if (symbol == Clutter.KEY_Left || symbol == Clutter.KEY_Right) { | ||||
| @@ -180,10 +181,10 @@ const Button = new Lang.Class({ | ||||
|             if (group) { | ||||
|                 let direction = (symbol == Clutter.KEY_Left) ? Gtk.DirectionType.LEFT : Gtk.DirectionType.RIGHT; | ||||
|                 group.navigate_focus(this.actor, direction, false); | ||||
|                 return true; | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onOpenStateChanged: function(menu, open) { | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Meta = imports.gi.Meta; | ||||
| @@ -110,7 +111,7 @@ const PointerWatcher = new Lang.Class({ | ||||
|  | ||||
|     _onTimeout: function() { | ||||
|         this._updatePointer(); | ||||
|         return true; | ||||
|         return GLib.SOURCE_CONTINUE; | ||||
|     }, | ||||
|  | ||||
|     _updatePointer: function() { | ||||
|   | ||||
| @@ -45,28 +45,35 @@ function isPopupMenuItemVisible(child) { | ||||
| /** | ||||
|  * @side Side to which the arrow points. | ||||
|  */ | ||||
| function unicodeArrow(side) { | ||||
|     let arrowChar; | ||||
| function arrowIcon(side) { | ||||
|     let rotation; | ||||
|     switch (side) { | ||||
|         case St.Side.TOP: | ||||
|             arrowChar = '\u25B4'; | ||||
|             rotation = 180; | ||||
|             break; | ||||
|         case St.Side.RIGHT: | ||||
|             arrowChar = '\u25B8'; | ||||
|             rotation = - 90; | ||||
|             break; | ||||
|         case St.Side.BOTTOM: | ||||
|             arrowChar = '\u25BE'; | ||||
|             rotation = 0; | ||||
|             break; | ||||
|         case St.Side.LEFT: | ||||
|             arrowChar = '\u25C2'; | ||||
|             rotation = 90; | ||||
|             break; | ||||
|     } | ||||
|  | ||||
|     return new St.Label({ text: arrowChar, | ||||
|                           style_class: 'unicode-arrow', | ||||
|                           accessible_role: Atk.Role.ARROW, | ||||
|                           y_expand: true, | ||||
|                           y_align: Clutter.ActorAlign.CENTER }); | ||||
|     let gicon = new Gio.FileIcon({ file: Gio.File.new_for_path(global.datadir + | ||||
|                                              '/theme/menu-arrow-symbolic.svg') }); | ||||
|  | ||||
|     let arrow = new St.Icon({ style_class: 'popup-menu-arrow', | ||||
|                               gicon: gicon, | ||||
|                               accessible_role: Atk.Role.ARROW, | ||||
|                               y_expand: true, | ||||
|                               y_align: Clutter.ActorAlign.CENTER }); | ||||
|  | ||||
|     arrow.rotation_angle_z = rotation; | ||||
|  | ||||
|     return arrow; | ||||
| } | ||||
|  | ||||
| const PopupBaseMenuItem = new Lang.Class({ | ||||
| @@ -111,6 +118,7 @@ const PopupBaseMenuItem = new Lang.Class({ | ||||
|  | ||||
|         this.actor.connect('key-focus-in', Lang.bind(this, this._onKeyFocusIn)); | ||||
|         this.actor.connect('key-focus-out', Lang.bind(this, this._onKeyFocusOut)); | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|     }, | ||||
|  | ||||
|     _getTopMenu: function() { | ||||
| @@ -126,7 +134,7 @@ const PopupBaseMenuItem = new Lang.Class({ | ||||
|  | ||||
|     _onButtonReleaseEvent: function (actor, event) { | ||||
|         this.activate(event); | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _onKeyPressEvent: function (actor, event) { | ||||
| @@ -134,9 +142,9 @@ const PopupBaseMenuItem = new Lang.Class({ | ||||
|  | ||||
|         if (symbol == Clutter.KEY_space || symbol == Clutter.KEY_Return) { | ||||
|             this.activate(event); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onKeyFocusIn: function (actor) { | ||||
| @@ -192,6 +200,9 @@ const PopupBaseMenuItem = new Lang.Class({ | ||||
|  | ||||
|     destroy: function() { | ||||
|         this.actor.destroy(); | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
|         this.emit('destroy'); | ||||
|     }, | ||||
|  | ||||
| @@ -868,14 +879,14 @@ const PopupSubMenu = new Lang.Class({ | ||||
|         if (animate) { | ||||
|             let [minHeight, naturalHeight] = this.actor.get_preferred_height(-1); | ||||
|             this.actor.height = 0; | ||||
|             this.actor._arrow_rotation = this._arrow.rotation_angle_z; | ||||
|             this.actor._arrowRotation = this._arrow.rotation_angle_z; | ||||
|             Tweener.addTween(this.actor, | ||||
|                              { _arrow_rotation: 90, | ||||
|                              { _arrowRotation: this.actor._arrowRotation + 90, | ||||
|                                height: naturalHeight, | ||||
|                                time: 0.25, | ||||
|                                onUpdateScope: this, | ||||
|                                onUpdate: function() { | ||||
|                                    this._arrow.rotation_angle_z = this.actor._arrow_rotation; | ||||
|                                    this._arrow.rotation_angle_z = this.actor._arrowRotation; | ||||
|                                }, | ||||
|                                onCompleteScope: this, | ||||
|                                onComplete: function() { | ||||
| @@ -883,7 +894,7 @@ const PopupSubMenu = new Lang.Class({ | ||||
|                                } | ||||
|                              }); | ||||
|         } else { | ||||
|             this._arrow.rotation_angle_z = 90; | ||||
|             this._arrow.rotation_angle_z = this.actor._arrowRotation + 90; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -901,14 +912,14 @@ const PopupSubMenu = new Lang.Class({ | ||||
|             animate = false; | ||||
|  | ||||
|         if (animate) { | ||||
|             this.actor._arrow_rotation = this._arrow.rotation_angle_z; | ||||
|             this.actor._arrowRotation = this._arrow.rotation_angle_z; | ||||
|             Tweener.addTween(this.actor, | ||||
|                              { _arrow_rotation: 0, | ||||
|                              { _arrowRotation: this.actor._arrowRotation - 90, | ||||
|                                height: 0, | ||||
|                                time: 0.25, | ||||
|                                onUpdateScope: this, | ||||
|                                onUpdate: function() { | ||||
|                                    this._arrow.rotation_angle_z = this.actor._arrow_rotation; | ||||
|                                    this._arrow.rotation_angle_z = this.actor._arrowRotation; | ||||
|                                }, | ||||
|                                onCompleteScope: this, | ||||
|                                onComplete: function() { | ||||
| @@ -917,7 +928,7 @@ const PopupSubMenu = new Lang.Class({ | ||||
|                                }, | ||||
|                              }); | ||||
|         } else { | ||||
|             this._arrow.rotation_angle_z = 0; | ||||
|             this._arrow.rotation_angle_z = this.actor._arrowRotation - 90; | ||||
|             this.actor.hide(); | ||||
|         } | ||||
|     }, | ||||
| @@ -928,10 +939,10 @@ const PopupSubMenu = new Lang.Class({ | ||||
|         if (this.isOpen && event.get_key_symbol() == Clutter.KEY_Left) { | ||||
|             this.close(BoxPointer.PopupAnimation.FULL); | ||||
|             this.sourceActor._delegate.setActive(true); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -989,7 +1000,7 @@ const PopupSubMenuMenuItem = new Lang.Class({ | ||||
|                                      y_align: Clutter.ActorAlign.CENTER }); | ||||
|         this.actor.add_child(this.status); | ||||
|  | ||||
|         this._triangle = unicodeArrow(St.Side.RIGHT); | ||||
|         this._triangle = arrowIcon(St.Side.RIGHT); | ||||
|         this._triangle.pivot_point = new Clutter.Point({ x: 0.5, y: 0.6 }); | ||||
|  | ||||
|         this._triangleBin = new St.Widget({ y_expand: true, | ||||
| @@ -1056,10 +1067,10 @@ const PopupSubMenuMenuItem = new Lang.Class({ | ||||
|         if (symbol == Clutter.KEY_Right) { | ||||
|             this._setOpenState(true); | ||||
|             this.menu.actor.navigate_focus(null, Gtk.DirectionType.DOWN, false); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } else if (symbol == Clutter.KEY_Left && this._getOpenState()) { | ||||
|             this._setOpenState(false); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|  | ||||
|         return this.parent(actor, event); | ||||
| @@ -1071,6 +1082,7 @@ const PopupSubMenuMenuItem = new Lang.Class({ | ||||
|  | ||||
|     _onButtonReleaseEvent: function(actor) { | ||||
|         this._setOpenState(!this._getOpenState()); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -1102,7 +1114,7 @@ const PopupMenuManager = new Lang.Class({ | ||||
|         if (source) { | ||||
|             if (!menu.blockSourceEvents) | ||||
|                 this._grabHelper.addActor(source); | ||||
|             menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { this._onMenuSourceEnter(menu); })); | ||||
|             menudata.enterId = source.connect('enter-event', Lang.bind(this, function() { return this._onMenuSourceEnter(menu); })); | ||||
|             menudata.focusInId = source.connect('key-focus-in', Lang.bind(this, function() { this._onMenuSourceEnter(menu); })); | ||||
|         } | ||||
|  | ||||
| @@ -1164,13 +1176,13 @@ const PopupMenuManager = new Lang.Class({ | ||||
|  | ||||
|     _onMenuSourceEnter: function(menu) { | ||||
|         if (!this._grabHelper.grabbed) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (this._grabHelper.isActorGrabbed(menu.actor)) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         this._changeMenu(menu); | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onMenuDestroy: function(menu) { | ||||
|   | ||||
| @@ -191,7 +191,9 @@ const RemoteSearchProvider = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     createIcon: function(size, meta) { | ||||
|         let gicon; | ||||
|         let gicon = null; | ||||
|         let icon = null; | ||||
|  | ||||
|         if (meta['icon']) { | ||||
|             gicon = Gio.icon_deserialize(meta['icon']); | ||||
|         } else if (meta['gicon']) { | ||||
| @@ -203,8 +205,10 @@ const RemoteSearchProvider = new Lang.Class({ | ||||
|                                                        bitsPerSample, width, height, rowStride); | ||||
|         } | ||||
|  | ||||
|         return new St.Icon({ gicon: gicon, | ||||
|                              icon_size: size }); | ||||
|         if (gicon) | ||||
|             icon = new St.Icon({ gicon: gicon, | ||||
|                                  icon_size: size }); | ||||
|         return icon; | ||||
|     }, | ||||
|  | ||||
|     filterResults: function(results, maxNumber) { | ||||
| @@ -279,7 +283,7 @@ const RemoteSearchProvider = new Lang.Class({ | ||||
|         // the provider is not compatible with the new version of the interface, launch | ||||
|         // the app itself but warn so we can catch the error in logs | ||||
|         log('Search provider ' + this.appInfo.get_id() + ' does not implement LaunchSearch'); | ||||
|         this.appInfo.launch([], global.create_app_launch_context()); | ||||
|         this.appInfo.launch([], global.create_app_launch_context(0, -1)); | ||||
|     } | ||||
| }); | ||||
|  | ||||
|   | ||||
| @@ -73,7 +73,9 @@ const RunDialog = new Lang.Class({ | ||||
|         let label = new St.Label({ style_class: 'run-dialog-label', | ||||
|                                    text: _("Enter a Command") }); | ||||
|  | ||||
|         this.contentLayout.add(label, { y_align: St.Align.START }); | ||||
|         this.contentLayout.add(label, { x_fill: false, | ||||
|                                         x_align: St.Align.START, | ||||
|                                         y_align: St.Align.START }); | ||||
|  | ||||
|         let entry = new St.Entry({ style_class: 'run-dialog-entry', | ||||
|                                    can_focus: true }); | ||||
| @@ -101,6 +103,8 @@ const RunDialog = new Lang.Class({ | ||||
|         this._errorMessage.clutter_text.line_wrap = true; | ||||
|  | ||||
|         this._errorBox.add(this._errorMessage, { expand: true, | ||||
|                                                  x_align: St.Align.START, | ||||
|                                                  x_fill: false, | ||||
|                                                  y_align: St.Align.MIDDLE, | ||||
|                                                  y_fill: false }); | ||||
|  | ||||
| @@ -124,7 +128,7 @@ const RunDialog = new Lang.Class({ | ||||
|                     !this.pushModal()) | ||||
|                     this.close(); | ||||
|  | ||||
|                 return true; | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } | ||||
|             if (symbol == Clutter.Tab) { | ||||
|                 let text = o.get_text(); | ||||
| @@ -138,9 +142,9 @@ const RunDialog = new Lang.Class({ | ||||
|                     o.insert_text(postfix, -1); | ||||
|                     o.set_cursor_position(text.length + postfix.length); | ||||
|                 } | ||||
|                 return true; | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
| @@ -229,7 +233,7 @@ const RunDialog = new Lang.Class({ | ||||
|                     let file = Gio.file_new_for_path(path); | ||||
|                     try { | ||||
|                         Gio.app_info_launch_default_for_uri(file.get_uri(), | ||||
|                                                             global.create_app_launch_context()); | ||||
|                                                             global.create_app_launch_context(0, -1)); | ||||
|                     } catch (e) { | ||||
|                         // The exception from gjs contains an error string like: | ||||
|                         //     Error invoking Gio.app_info_launch_default_for_uri: No application | ||||
|   | ||||
| @@ -17,7 +17,6 @@ const TweenerEquations = imports.tweener.equations; | ||||
|  | ||||
| const Background = imports.ui.background; | ||||
| const GnomeSession = imports.misc.gnomeSession; | ||||
| const Hash = imports.misc.hash; | ||||
| const Layout = imports.ui.layout; | ||||
| const OVirt = imports.gdm.oVirt; | ||||
| const LoginManager = imports.misc.loginManager; | ||||
| @@ -115,7 +114,7 @@ const NotificationsBox = new Lang.Class({ | ||||
|         this.actor.add(this._musicBin); | ||||
|         this.actor.add(this._scrollView, { x_fill: true, x_align: St.Align.START }); | ||||
|  | ||||
|         this._sources = new Hash.Map(); | ||||
|         this._sources = new Map(); | ||||
|         Main.messageTray.getSources().forEach(Lang.bind(this, function(source) { | ||||
|             this._sourceAdded(Main.messageTray, source, true); | ||||
|         })); | ||||
| @@ -130,9 +129,8 @@ const NotificationsBox = new Lang.Class({ | ||||
|             this._sourceAddedId = 0; | ||||
|         } | ||||
|  | ||||
|         let items = this._sources.items(); | ||||
|         for (let i = 0; i < items.length; i++) { | ||||
|             let [source, obj] = items[i]; | ||||
|         let items = this._sources.entries(); | ||||
|         for (let [source, obj] of items) { | ||||
|             this._removeSource(source, obj); | ||||
|         } | ||||
|  | ||||
| @@ -240,10 +238,6 @@ const NotificationsBox = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _sourceAdded: function(tray, source, initial) { | ||||
|         // Ignore transient sources | ||||
|         if (source.isTransient) | ||||
|             return; | ||||
|  | ||||
|         let obj = { | ||||
|             visible: source.policy.showInLockScreen, | ||||
|             detailed: source.policy.detailsInLockScreen, | ||||
| @@ -307,7 +301,7 @@ const NotificationsBox = new Lang.Class({ | ||||
|                              }); | ||||
|  | ||||
|             this._updateVisibility(); | ||||
|             Shell.util_wake_up_screen(); | ||||
|             this.emit('wake-up-screen'); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -333,7 +327,7 @@ const NotificationsBox = new Lang.Class({ | ||||
|  | ||||
|         this._updateVisibility(); | ||||
|         if (obj.sourceBox.visible) | ||||
|             Shell.util_wake_up_screen(); | ||||
|             this.emit('wake-up-screen'); | ||||
|     }, | ||||
|  | ||||
|     _visibleChanged: function(source, obj) { | ||||
| @@ -348,7 +342,7 @@ const NotificationsBox = new Lang.Class({ | ||||
|  | ||||
|         this._updateVisibility(); | ||||
|         if (obj.sourceBox.visible) | ||||
|             Shell.util_wake_up_screen(); | ||||
|             this.emit('wake-up-screen'); | ||||
|     }, | ||||
|  | ||||
|     _detailedChanged: function(source, obj) { | ||||
| @@ -386,6 +380,7 @@ const NotificationsBox = new Lang.Class({ | ||||
|         this._sources.delete(source); | ||||
|     }, | ||||
| }); | ||||
| Signals.addSignalMethods(NotificationsBox.prototype); | ||||
|  | ||||
| const Arrow = new Lang.Class({ | ||||
|     Name: 'Arrow', | ||||
| @@ -419,6 +414,7 @@ const Arrow = new Lang.Class({ | ||||
|         cr.lineTo(w/2, thickness); | ||||
|         cr.lineTo(w - thickness / 2, h - thickness / 2); | ||||
|         cr.stroke(); | ||||
|         cr.$dispose(); | ||||
|     }, | ||||
|  | ||||
|     vfunc_style_changed: function() { | ||||
| @@ -541,7 +537,7 @@ const ScreenShield = new Lang.Class({ | ||||
|  | ||||
|         this._smartcardManager = SmartcardManager.getSmartcardManager(); | ||||
|         this._smartcardManager.connect('smartcard-inserted', | ||||
|                                        Lang.bind(this, function(token) { | ||||
|                                        Lang.bind(this, function(manager, token) { | ||||
|                                            if (this._isLocked && token.UsedToLogin) | ||||
|                                                this._liftShield(true, 0); | ||||
|                                        })); | ||||
| @@ -673,11 +669,11 @@ const ScreenShield = new Lang.Class({ | ||||
|         // down after cancel. | ||||
|  | ||||
|         if (this._lockScreenState != MessageTray.State.SHOWN) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter); | ||||
|         if (!isEnter && !(GLib.unichar_isprint(unichar) || symbol == Clutter.KEY_Escape)) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (this._isLocked && | ||||
|             this._ensureUnlockDialog(true, true) && | ||||
| @@ -685,12 +681,12 @@ const ScreenShield = new Lang.Class({ | ||||
|             this._dialog.addCharacter(unichar); | ||||
|  | ||||
|         this._liftShield(true, 0); | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _onLockScreenScroll: function(actor, event) { | ||||
|         if (this._lockScreenState != MessageTray.State.SHOWN) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let delta = 0; | ||||
|         if (event.get_scroll_direction() == Clutter.ScrollDirection.UP) | ||||
| @@ -705,7 +701,7 @@ const ScreenShield = new Lang.Class({ | ||||
|             this._liftShield(true, 0); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _inhibitSuspend: function() { | ||||
| @@ -733,7 +729,7 @@ const ScreenShield = new Lang.Class({ | ||||
|         } else { | ||||
|             this._inhibitSuspend(); | ||||
|  | ||||
|             this._onUserBecameActive(); | ||||
|             this._wakeUpScreen(); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -756,7 +752,7 @@ const ScreenShield = new Lang.Class({ | ||||
|                              }); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|         return GLib.SOURCE_CONTINUE; | ||||
|     }, | ||||
|  | ||||
|     _onDragBegin: function() { | ||||
| @@ -852,7 +848,7 @@ const ScreenShield = new Lang.Class({ | ||||
|                                                        Lang.bind(this, function() { | ||||
|                                                            this._lockTimeoutId = 0; | ||||
|                                                            this.lock(false); | ||||
|                                                            return false; | ||||
|                                                            return GLib.SOURCE_REMOVE; | ||||
|                                                        })); | ||||
|         } | ||||
|  | ||||
| @@ -905,17 +901,11 @@ const ScreenShield = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     showDialog: function() { | ||||
|         // Ensure that the stage window is mapped, before taking a grab | ||||
|         // otherwise X errors out | ||||
|         Meta.later_add(Meta.LaterType.BEFORE_REDRAW, Lang.bind(this, function() { | ||||
|             if (!this._becomeModal()) { | ||||
|                 // In the login screen, this is a hard error. Fail-whale | ||||
|                 log('Could not acquire modal grab for the login screen. Aborting login process.'); | ||||
|                 Meta.quit(Meta.ExitCode.ERROR); | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|         })); | ||||
|         if (!this._becomeModal()) { | ||||
|             // In the login screen, this is a hard error. Fail-whale | ||||
|             log('Could not acquire modal grab for the login screen. Aborting login process.'); | ||||
|             Meta.quit(Meta.ExitCode.ERROR); | ||||
|         } | ||||
|  | ||||
|         this.actor.show(); | ||||
|         this._isGreeter = Main.sessionMode.isGreeter; | ||||
| @@ -1101,7 +1091,7 @@ const ScreenShield = new Lang.Class({ | ||||
|                 global.stage.disconnect(motionId); | ||||
|             } | ||||
|  | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         })); | ||||
|         this._cursorTracker.set_pointer_visible(false); | ||||
|  | ||||
| @@ -1114,6 +1104,7 @@ const ScreenShield = new Lang.Class({ | ||||
|  | ||||
|             Mainloop.timeout_add(1000 * MANUAL_FADE_TIME, Lang.bind(this, function() { | ||||
|                 this._activateFade(this._shortLightbox, MANUAL_FADE_TIME); | ||||
|                 return GLib.SOURCE_REMOVE; | ||||
|             })); | ||||
|         } else { | ||||
|             if (params.fadeToBlack) | ||||
| @@ -1155,6 +1146,7 @@ const ScreenShield = new Lang.Class({ | ||||
|         this._lockScreenContents.add_actor(this._lockScreenContentsBox); | ||||
|  | ||||
|         this._notificationsBox = new NotificationsBox(); | ||||
|         this._wakeUpScreenId = this._notificationsBox.connect('wake-up-screen', Lang.bind(this, this._wakeUpScreen)); | ||||
|         this._lockScreenContentsBox.add(this._notificationsBox.actor, { x_fill: true, | ||||
|                                                                         y_fill: true, | ||||
|                                                                         expand: true }); | ||||
| @@ -1162,11 +1154,17 @@ const ScreenShield = new Lang.Class({ | ||||
|         this._hasLockScreen = true; | ||||
|     }, | ||||
|  | ||||
|     _wakeUpScreen: function() { | ||||
|         this._onUserBecameActive(); | ||||
|         this.emit('wake-up-screen'); | ||||
|     }, | ||||
|  | ||||
|     _clearLockScreen: function() { | ||||
|         this._clock.destroy(); | ||||
|         this._clock = null; | ||||
|  | ||||
|         if (this._notificationsBox) { | ||||
|             this._notificationsBox.disconnect(this._wakeUpScreenId); | ||||
|             this._notificationsBox.destroy(); | ||||
|             this._notificationsBox = null; | ||||
|         } | ||||
|   | ||||
| @@ -6,7 +6,6 @@ const Lang = imports.lang; | ||||
| const Shell = imports.gi.Shell; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Hash = imports.misc.hash; | ||||
| const Main = imports.ui.main; | ||||
|  | ||||
| const ScreencastIface = '<node> \ | ||||
| @@ -42,13 +41,13 @@ const ScreencastService = new Lang.Class({ | ||||
|  | ||||
|         Gio.DBus.session.own_name('org.gnome.Shell.Screencast', Gio.BusNameOwnerFlags.REPLACE, null, null); | ||||
|  | ||||
|         this._recorders = new Hash.Map(); | ||||
|         this._recorders = new Map(); | ||||
|  | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); | ||||
|     }, | ||||
|  | ||||
|     get isRecording() { | ||||
|         return this._recorders.size() > 0; | ||||
|         return this._recorders.size > 0; | ||||
|     }, | ||||
|  | ||||
|     _ensureRecorderForSender: function(sender) { | ||||
| @@ -69,8 +68,7 @@ const ScreencastService = new Lang.Class({ | ||||
|         if (Main.sessionMode.allowScreencast) | ||||
|             return; | ||||
|  | ||||
|         for (let sender in this._recorders.keys()) | ||||
|             this._recorders.delete(sender); | ||||
|         this._recorders.clear(); | ||||
|         this.emit('updated'); | ||||
|     }, | ||||
|  | ||||
|   | ||||
| @@ -67,6 +67,13 @@ const ScreenshotService = new Lang.Class({ | ||||
|         Gio.DBus.session.own_name('org.gnome.Shell.Screenshot', Gio.BusNameOwnerFlags.REPLACE, null, null); | ||||
|     }, | ||||
|  | ||||
|     _checkArea: function(x, y, width, height) { | ||||
|         return x >= 0 && y >= 0 && | ||||
|                width > 0 && height > 0 && | ||||
|                x + width <= global.screen_width && | ||||
|                y + height <= global.screen_height; | ||||
|     }, | ||||
|  | ||||
|     _onScreenshotComplete: function(obj, result, area, filenameUsed, flash, invocation) { | ||||
|         if (flash && result) { | ||||
|             let flashspot = new Flashspot(area); | ||||
| @@ -77,13 +84,31 @@ const ScreenshotService = new Lang.Class({ | ||||
|         invocation.return_value(retval); | ||||
|     }, | ||||
|  | ||||
|     _scaleArea: function(x, y, width, height) { | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|         x *= scaleFactor; | ||||
|         y *= scaleFactor; | ||||
|         width *= scaleFactor; | ||||
|         height *= scaleFactor; | ||||
|         return [x, y, width, height]; | ||||
|     }, | ||||
|  | ||||
|     _unscaleArea: function(x, y, width, height) { | ||||
|         let scaleFactor = St.ThemeContext.get_for_stage(global.stage).scale_factor; | ||||
|         x /= scaleFactor; | ||||
|         y /= scaleFactor; | ||||
|         width /= scaleFactor; | ||||
|         height /= scaleFactor; | ||||
|         return [x, y, width, height]; | ||||
|     }, | ||||
|  | ||||
|     ScreenshotAreaAsync : function (params, invocation) { | ||||
|         let [x, y, width, height, flash, filename, callback] = params; | ||||
|         if (x < 0 || y < 0 || | ||||
|             width <= 0 || height <= 0 || | ||||
|             x + width > global.screen_width || y + height > global.screen_height) { | ||||
|             invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED, | ||||
|                         "Invalid params"); | ||||
|         [x, y, width, height] = this._scaleArea(x, y, width, height); | ||||
|         if (!this._checkArea(x, y, width, height)) { | ||||
|             invocation.return_error_literal(Gio.IOErrorEnum, | ||||
|                                             Gio.IOErrorEnum.CANCELLED, | ||||
|                                             "Invalid params"); | ||||
|             return; | ||||
|         } | ||||
|         let screenshot = new Shell.Screenshot(); | ||||
| @@ -114,9 +139,9 @@ const ScreenshotService = new Lang.Class({ | ||||
|         selectArea.connect('finished', Lang.bind(this, | ||||
|             function(selectArea, areaRectangle) { | ||||
|                 if (areaRectangle) { | ||||
|                     let retval = GLib.Variant.new('(iiii)', | ||||
|                         [areaRectangle.x, areaRectangle.y, | ||||
|                          areaRectangle.width, areaRectangle.height]); | ||||
|                     let retRectangle = this._unscaleArea(areaRectangle.x, areaRectangle.y, | ||||
|                         areaRectangle.width, areaRectangle.height); | ||||
|                     let retval = GLib.Variant.new('(iiii)', retRectangle); | ||||
|                     invocation.return_value(retval); | ||||
|                 } else { | ||||
|                     invocation.return_error_literal(Gio.IOErrorEnum, Gio.IOErrorEnum.CANCELLED, | ||||
| @@ -125,9 +150,18 @@ const ScreenshotService = new Lang.Class({ | ||||
|             })); | ||||
|     }, | ||||
|  | ||||
|     FlashArea: function(x, y, width, height) { | ||||
|     FlashAreaAsync: function(params, invocation) { | ||||
|         let [x, y, width, height] = params; | ||||
|         [x, y, width, height] = this._scaleArea(x, y, width, height); | ||||
|         if (!this._checkArea(x, y, width, height)) { | ||||
|             invocation.return_error_literal(Gio.IOErrorEnum, | ||||
|                                             Gio.IOErrorEnum.CANCELLED, | ||||
|                                             "Invalid params"); | ||||
|             return; | ||||
|         } | ||||
|         let flashspot = new Flashspot({ x : x, y : y, width: width, height: height}); | ||||
|         flashspot.fire(); | ||||
|         invocation.return_value(null); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -206,12 +240,12 @@ const SelectArea = new Lang.Class({ | ||||
|         if (event.get_key_symbol() == Clutter.Escape) | ||||
|             this._destroy(null, false); | ||||
|  | ||||
|         return; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onMotionEvent: function(actor, event) { | ||||
|         if (this._startX == -1 || this._startY == -1) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         [this._lastX, this._lastY] = event.get_coords(); | ||||
|         let geometry = this._getGeometry(); | ||||
| @@ -219,19 +253,19 @@ const SelectArea = new Lang.Class({ | ||||
|         this._rubberband.set_position(geometry.x, geometry.y); | ||||
|         this._rubberband.set_size(geometry.width, geometry.height); | ||||
|  | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onButtonPress: function(actor, event) { | ||||
|         [this._startX, this._startY] = event.get_coords(); | ||||
|         this._rubberband.set_position(this._startX, this._startY); | ||||
|  | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onButtonRelease: function(actor, event) { | ||||
|         this._destroy(this._getGeometry(), true); | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _destroy: function(geometry, fade) { | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Shell = imports.gi.Shell; | ||||
| @@ -41,7 +42,7 @@ function sleep(milliseconds) { | ||||
|     Mainloop.timeout_add(milliseconds, function() { | ||||
|                              if (cb) | ||||
|                                  cb(); | ||||
|                              return false; | ||||
|                              return GLib.SOURCE_REMOVE; | ||||
|                          }); | ||||
|  | ||||
|     return function(callback) { | ||||
|   | ||||
| @@ -68,6 +68,9 @@ const SearchSystem = new Lang.Class({ | ||||
|     _unregisterProvider: function (provider) { | ||||
|         let index = this._providers.indexOf(provider); | ||||
|         this._providers.splice(index, 1); | ||||
|  | ||||
|         if (provider.display) | ||||
|             provider.display.destroy(); | ||||
|     }, | ||||
|  | ||||
|     getProviders: function() { | ||||
| @@ -315,6 +318,8 @@ const SearchResultsBase = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     clear: function() { | ||||
|         for (let resultId in this._resultDisplays) | ||||
|             this._resultDisplays[resultId].actor.destroy(); | ||||
|         this._resultDisplays = {}; | ||||
|         this._clearResultDisplay(); | ||||
|         this.actor.hide(); | ||||
| @@ -338,12 +343,22 @@ const SearchResultsBase = new Lang.Class({ | ||||
|         })); | ||||
|  | ||||
|         if (metasNeeded.length === 0) { | ||||
|             callback(); | ||||
|             callback(true); | ||||
|         } else { | ||||
|             this._cancellable.cancel(); | ||||
|             this._cancellable.reset(); | ||||
|  | ||||
|             this.provider.getResultMetas(metasNeeded, Lang.bind(this, function(metas) { | ||||
|                 if (metas.length == 0) { | ||||
|                     callback(false); | ||||
|                     return; | ||||
|                 } | ||||
|                 if (metas.length != metasNeeded.length) { | ||||
|                     log('Wrong number of result metas returned by search provider'); | ||||
|                     callback(false); | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 metasNeeded.forEach(Lang.bind(this, function(resultId, i) { | ||||
|                     let meta = metas[i]; | ||||
|                     let display = this._createResultDisplay(meta); | ||||
| @@ -351,7 +366,7 @@ const SearchResultsBase = new Lang.Class({ | ||||
|                     display.actor.connect('key-focus-in', Lang.bind(this, this._keyFocusIn)); | ||||
|                     this._resultDisplays[resultId] = display; | ||||
|                 })); | ||||
|                 callback(); | ||||
|                 callback(true); | ||||
|             }), this._cancellable); | ||||
|         } | ||||
|     }, | ||||
| @@ -368,8 +383,10 @@ const SearchResultsBase = new Lang.Class({ | ||||
|             let results = this.provider.filterResults(providerResults, maxResults); | ||||
|             let hasMoreResults = results.length < providerResults.length; | ||||
|  | ||||
|             this._ensureResultActors(results, Lang.bind(this, function() { | ||||
|             this._ensureResultActors(results, Lang.bind(this, function(successful) { | ||||
|                 this._clearResultDisplay(); | ||||
|                 if (!successful) | ||||
|                     return; | ||||
|  | ||||
|                 // To avoid CSS transitions causing flickering when | ||||
|                 // the first search result stays the same, we hide the | ||||
| @@ -615,8 +632,11 @@ const SearchResults = new Lang.Class({ | ||||
|         if (newDefaultResult != this._defaultResult) { | ||||
|             if (this._defaultResult) | ||||
|                 this._defaultResult.setSelected(false); | ||||
|             if (newDefaultResult) | ||||
|             if (newDefaultResult) { | ||||
|                 newDefaultResult.setSelected(this._highlightDefault); | ||||
|                 if (this._highlightDefault) | ||||
|                     Util.ensureActorVisibleInScrollView(this._scrollView, newDefaultResult.actor); | ||||
|             } | ||||
|  | ||||
|             this._defaultResult = newDefaultResult; | ||||
|         } | ||||
| @@ -653,8 +673,11 @@ const SearchResults = new Lang.Class({ | ||||
|  | ||||
|     highlightDefault: function(highlight) { | ||||
|         this._highlightDefault = highlight; | ||||
|         if (this._defaultResult) | ||||
|         if (this._defaultResult) { | ||||
|             this._defaultResult.setSelected(highlight); | ||||
|             if (highlight) | ||||
|                 Util.ensureActorVisibleInScrollView(this._scrollView, this._defaultResult.actor); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     navigateFocus: function(direction) { | ||||
|   | ||||
| @@ -10,6 +10,8 @@ const FileUtils = imports.misc.fileUtils; | ||||
| const Main = imports.ui.main; | ||||
| const Params = imports.misc.params; | ||||
|  | ||||
| const Config = imports.misc.config; | ||||
|  | ||||
| const DEFAULT_MODE = 'restrictive'; | ||||
|  | ||||
| const _modes = { | ||||
| @@ -92,8 +94,12 @@ const _modes = { | ||||
|         isLocked: false, | ||||
|         isPrimary: true, | ||||
|         unlockDialog: imports.ui.unlockDialog.UnlockDialog, | ||||
|         components: ['networkAgent', 'polkitAgent', 'telepathyClient', | ||||
|         components: Config.HAVE_NETWORKMANAGER ? | ||||
|                     ['networkAgent', 'polkitAgent', 'telepathyClient', | ||||
|                      'keyring', 'autorunManager', 'automountManager'] : | ||||
|                     ['polkitAgent', 'telepathyClient', | ||||
|                      'keyring', 'autorunManager', 'automountManager'], | ||||
|  | ||||
|         panel: { | ||||
|             left: ['activities', 'appMenu'], | ||||
|             center: ['dateMenu'], | ||||
| @@ -120,22 +126,22 @@ function _loadMode(file, info) { | ||||
|  | ||||
|     _modes[modeName] = {}; | ||||
|     let propBlacklist = ['unlockDialog']; | ||||
|     for (let prop in loadedData[DEFAULT_MODE]) { | ||||
|     for (let prop in _modes[DEFAULT_MODE]) { | ||||
|         if (newMode[prop] !== undefined && | ||||
|             propBlacklist.indexOf(prop) == -1) | ||||
|             loadedData[modeName][prop] = newMode[prop]; | ||||
|             _modes[modeName][prop] = newMode[prop]; | ||||
|     } | ||||
|     _modes[modeName]['isPrimary'] = true; | ||||
| } | ||||
|  | ||||
| function _getModes() { | ||||
| function _loadModes() { | ||||
|     FileUtils.collectFromDatadirs('modes', false, _loadMode); | ||||
| } | ||||
|  | ||||
| function listModes() { | ||||
|     let modes = _getModes(); | ||||
|     modes.forEach(function() { | ||||
|         let names = Object.getOwnPropertyNames(modes); | ||||
|     _loadModes(); | ||||
|     Mainloop.idle_add(function() { | ||||
|         let names = Object.getOwnPropertyNames(_modes); | ||||
|         for (let i = 0; i < names.length; i++) | ||||
|             if (_modes[names[i]].isPrimary) | ||||
|                 print(names[i]); | ||||
| @@ -148,6 +154,7 @@ const SessionMode = new Lang.Class({ | ||||
|     Name: 'SessionMode', | ||||
|  | ||||
|     _init: function() { | ||||
|         _loadModes(); | ||||
|         let isPrimary = (_modes[global.session_mode] && | ||||
|                          _modes[global.session_mode].isPrimary); | ||||
|         let mode = isPrimary ? global.session_mode : 'user'; | ||||
|   | ||||
| @@ -10,7 +10,6 @@ const Config = imports.misc.config; | ||||
| const ExtensionSystem = imports.ui.extensionSystem; | ||||
| const ExtensionDownloader = imports.ui.extensionDownloader; | ||||
| const ExtensionUtils = imports.misc.extensionUtils; | ||||
| const Hash = imports.misc.hash; | ||||
| const Main = imports.ui.main; | ||||
| const Screenshot = imports.ui.screenshot; | ||||
| const ViewSelector = imports.ui.viewSelector; | ||||
| @@ -70,6 +69,7 @@ const ScreenSaverIface = '<node> \ | ||||
| <signal name="ActiveChanged"> \ | ||||
|     <arg name="new_value" type="b" /> \ | ||||
| </signal> \ | ||||
| <signal name="WakeUpScreen" /> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| @@ -83,8 +83,8 @@ const GnomeShell = new Lang.Class({ | ||||
|         this._extensionsService = new GnomeShellExtensions(); | ||||
|         this._screenshotService = new Screenshot.ScreenshotService(); | ||||
|  | ||||
|         this._grabbedAccelerators = new Hash.Map(); | ||||
|         this._grabbers = new Hash.Map(); | ||||
|         this._grabbedAccelerators = new Map(); | ||||
|         this._grabbers = new Map(); | ||||
|  | ||||
|         global.display.connect('accelerator-activated', Lang.bind(this, | ||||
|             function(display, action, deviceid, timestamp) { | ||||
| @@ -133,11 +133,16 @@ const GnomeShell = new Lang.Class({ | ||||
|         for (let param in params) | ||||
|             params[param] = params[param].deep_unpack(); | ||||
|  | ||||
|         let monitorIndex = -1; | ||||
|         if (params['monitor']) | ||||
|             monitorIndex = params['monitor']; | ||||
|  | ||||
|         let icon = null; | ||||
|         if (params['icon']) | ||||
|             icon = Gio.Icon.new_for_string(params['icon']); | ||||
|  | ||||
|         Main.osdWindow.setIcon(icon); | ||||
|         Main.osdWindow.setMonitor (monitorIndex); | ||||
|         Main.osdWindow.setLabel(params['label']); | ||||
|         Main.osdWindow.setLevel(params['level']); | ||||
|  | ||||
| @@ -223,9 +228,8 @@ const GnomeShell = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onGrabberBusNameVanished: function(connection, name) { | ||||
|         let grabs = this._grabbedAccelerators.items(); | ||||
|         for (let i = 0; i < grabs.length; i++) { | ||||
|             let [action, sender] = grabs[i]; | ||||
|         let grabs = this._grabbedAccelerators.entries(); | ||||
|         for (let [action, sender] of grabs) { | ||||
|             if (sender == name) | ||||
|                 this._ungrabAccelerator(action); | ||||
|         } | ||||
| @@ -368,8 +372,10 @@ const GnomeShellExtensions = new Lang.Class({ | ||||
|     LaunchExtensionPrefs: function(uuid) { | ||||
|         let appSys = Shell.AppSystem.get_default(); | ||||
|         let app = appSys.lookup_app('gnome-shell-extension-prefs.desktop'); | ||||
|         app.launch(global.display.get_current_time_roundtrip(), | ||||
|                    ['extension:///' + uuid], -1, null); | ||||
|         let info = app.get_app_info(); | ||||
|         let timestamp = global.display.get_current_time_roundtrip(); | ||||
|         info.launch_uris(['extension:///' + uuid], | ||||
|                          global.create_app_launch_context(timestamp, -1)); | ||||
|     }, | ||||
|  | ||||
|     ReloadExtension: function(uuid) { | ||||
| @@ -402,6 +408,9 @@ const ScreenSaverDBus = new Lang.Class({ | ||||
|         screenShield.connect('active-changed', Lang.bind(this, function(shield) { | ||||
|             this._dbusImpl.emit_signal('ActiveChanged', GLib.Variant.new('(b)', [shield.active])); | ||||
|         })); | ||||
|         screenShield.connect('wake-up-screen', Lang.bind(this, function(shield) { | ||||
|             this._dbusImpl.emit_signal('WakeUpScreen', null); | ||||
|         })); | ||||
|  | ||||
|         this._dbusImpl = Gio.DBusExportedObject.wrapJSObject(ScreenSaverIface, this); | ||||
|         this._dbusImpl.export(Gio.DBus.session, '/org/gnome/ScreenSaver'); | ||||
|   | ||||
| @@ -132,14 +132,14 @@ function _setMenuAlignment(entry, stageX) { | ||||
| function _onButtonPressEvent(actor, event, entry) { | ||||
|     if (entry.menu.isOpen) { | ||||
|         entry.menu.close(BoxPointer.PopupAnimation.FULL); | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } else if (event.get_button() == 3) { | ||||
|         let [stageX, stageY] = event.get_coords(); | ||||
|         _setMenuAlignment(entry, stageX); | ||||
|         entry.menu.open(BoxPointer.PopupAnimation.FULL); | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
|     return false; | ||||
|     return Clutter.EVENT_PROPAGATE; | ||||
| }; | ||||
|  | ||||
| function _onPopup(actor, entry) { | ||||
|   | ||||
| @@ -1,11 +1,11 @@ | ||||
| /* -*- mode: js2; js2-basic-offset: 4; indent-tabs-mode: nil -*- */ | ||||
|  | ||||
| const Atk = imports.gi.Atk; | ||||
| const Cairo = imports.cairo; | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Lang = imports.lang; | ||||
| const St = imports.gi.St; | ||||
| const Signals = imports.signals; | ||||
| const Atk = imports.gi.Atk; | ||||
|  | ||||
| const SLIDER_SCROLL_STEP = 0.05; /* Slider scrolling step in % */ | ||||
|  | ||||
| @@ -111,12 +111,12 @@ const Slider = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _startDragging: function(actor, event) { | ||||
|         this.startDragging(event); | ||||
|         return this.startDragging(event); | ||||
|     }, | ||||
|  | ||||
|     startDragging: function(event) { | ||||
|         if (this._dragging) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         this._dragging = true; | ||||
|  | ||||
| @@ -129,7 +129,7 @@ const Slider = new Lang.Class({ | ||||
|         let absX, absY; | ||||
|         [absX, absY] = event.get_coords(); | ||||
|         this._moveHandle(absX, absY); | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _endDragging: function() { | ||||
| @@ -143,7 +143,7 @@ const Slider = new Lang.Class({ | ||||
|  | ||||
|             this.emit('drag-end'); | ||||
|         } | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     scroll: function(event) { | ||||
| @@ -151,7 +151,7 @@ const Slider = new Lang.Class({ | ||||
|         let delta; | ||||
|  | ||||
|         if (event.is_pointer_emulated()) | ||||
|             return; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         if (direction == Clutter.ScrollDirection.DOWN) { | ||||
|             delta = -SLIDER_SCROLL_STEP; | ||||
| @@ -168,17 +168,18 @@ const Slider = new Lang.Class({ | ||||
|  | ||||
|         this.actor.queue_repaint(); | ||||
|         this.emit('value-changed', this._value); | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _onScrollEvent: function(actor, event) { | ||||
|         this.scroll(event); | ||||
|         return this.scroll(event); | ||||
|     }, | ||||
|  | ||||
|     _motionEvent: function(actor, event) { | ||||
|         let absX, absY; | ||||
|         [absX, absY] = event.get_coords(); | ||||
|         this._moveHandle(absX, absY); | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     onKeyPressEvent: function (actor, event) { | ||||
| @@ -189,9 +190,9 @@ const Slider = new Lang.Class({ | ||||
|             this.actor.queue_repaint(); | ||||
|             this.emit('value-changed', this._value); | ||||
|             this.emit('drag-end'); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _moveHandle: function(absX, absY) { | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const St = imports.gi.St; | ||||
| @@ -43,7 +44,7 @@ const ATIndicator = new Lang.Class({ | ||||
|         this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' }); | ||||
|         this._hbox.add_child(new St.Icon({ style_class: 'system-status-icon', | ||||
|                                            icon_name: 'preferences-desktop-accessibility-symbolic' })); | ||||
|         this._hbox.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM)); | ||||
|         this._hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM)); | ||||
|  | ||||
|         this.actor.add_child(this._hbox); | ||||
|  | ||||
| @@ -94,7 +95,7 @@ const ATIndicator = new Lang.Class({ | ||||
|  | ||||
|         this.actor.visible = alwaysShow || items.some(function(f) { return !!f.state; }); | ||||
|  | ||||
|         return false; | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     _queueSyncMenuVisibility: function() { | ||||
|   | ||||
| @@ -2,16 +2,27 @@ | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GLib = imports.gi.GLib; | ||||
| const GnomeBluetoothApplet = imports.gi.GnomeBluetoothApplet; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GnomeBluetooth = imports.gi.GnomeBluetooth; | ||||
| const Lang = imports.lang; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
|  | ||||
| const BUS_NAME = 'org.gnome.SettingsDaemon.Rfkill'; | ||||
| const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill'; | ||||
|  | ||||
| const RfkillManagerInterface = '<node> \ | ||||
| <interface name="org.gnome.SettingsDaemon.Rfkill"> \ | ||||
| <property name="BluetoothAirplaneMode" type="b" access="readwrite" /> \ | ||||
| <property name="BluetoothHasAirplaneMode" type="b" access="read" /> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface); | ||||
|  | ||||
| const Indicator = new Lang.Class({ | ||||
|     Name: 'BTIndicator', | ||||
|     Extends: PanelMenu.SystemIndicator, | ||||
| @@ -22,238 +33,76 @@ const Indicator = new Lang.Class({ | ||||
|         this._indicator = this._addIndicator(); | ||||
|         this._indicator.icon_name = 'bluetooth-active-symbolic'; | ||||
|  | ||||
|         this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH, | ||||
|                                              Lang.bind(this, function(proxy, error) { | ||||
|                                                  if (error) { | ||||
|                                                      log(error.message); | ||||
|                                                      return; | ||||
|                                                  } | ||||
|  | ||||
|                                                  this._sync(); | ||||
|                                              })); | ||||
|         this._proxy.connect('g-properties-changed', Lang.bind(this, this._sync)); | ||||
|  | ||||
|         // The Bluetooth menu only appears when Bluetooth is in use, | ||||
|         // so just statically build it with a "Turn Off" menu item. | ||||
|         this._item = new PopupMenu.PopupSubMenuMenuItem(_("Bluetooth"), true); | ||||
|         this._item.icon.icon_name = 'bluetooth-active-symbolic'; | ||||
|         this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() { | ||||
|             this._applet.killswitch_state = GnomeBluetooth.KillswitchState.SOFT_BLOCKED; | ||||
|             this._proxy.BluetoothAirplaneMode = true; | ||||
|         })); | ||||
|         this._item.menu.addSettingsAction(_("Bluetooth Settings"), 'gnome-bluetooth-panel.desktop'); | ||||
|         this.menu.addMenuItem(this._item); | ||||
|  | ||||
|         this._applet = new GnomeBluetoothApplet.Applet(); | ||||
|         this._applet.connect('devices-changed', Lang.bind(this, this._sync)); | ||||
|         this._client = new GnomeBluetooth.Client(); | ||||
|         this._model = this._client.get_model(); | ||||
|         this._model.connect('row-changed', Lang.bind(this, this._sync)); | ||||
|         this._model.connect('row-deleted', Lang.bind(this, this._sync)); | ||||
|         this._model.connect('row-inserted', Lang.bind(this, this._sync)); | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._sync)); | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|         this._applet.connect('pincode-request', Lang.bind(this, this._pinRequest)); | ||||
|         this._applet.connect('confirm-request', Lang.bind(this, this._confirmRequest)); | ||||
|         this._applet.connect('auth-request', Lang.bind(this, this._authRequest)); | ||||
|         this._applet.connect('auth-service-request', Lang.bind(this, this._authServiceRequest)); | ||||
|         this._applet.connect('cancel-request', Lang.bind(this, this._cancelRequest)); | ||||
|     _getDefaultAdapter: function() { | ||||
|         let [ret, iter] = this._model.get_iter_first(); | ||||
|         while (ret) { | ||||
|             let isDefault = this._model.get_value(iter, | ||||
|                                                   GnomeBluetooth.Column.DEFAULT); | ||||
|             if (isDefault) | ||||
|                 return iter; | ||||
|             ret = this._model.iter_next(iter); | ||||
|         } | ||||
|         return null; | ||||
|     }, | ||||
|  | ||||
|     _getNConnectedDevices: function() { | ||||
|         let adapter = this._getDefaultAdapter(); | ||||
|         if (!adapter) | ||||
|             return 0; | ||||
|  | ||||
|         let nDevices = 0; | ||||
|         let [ret, iter] = this._model.iter_children(adapter); | ||||
|         while (ret) { | ||||
|             let isConnected = this._model.get_value(iter, | ||||
|                                                     GnomeBluetooth.Column.CONNECTED); | ||||
|             if (isConnected) | ||||
|                 nDevices++; | ||||
|             ret = this._model.iter_next(iter); | ||||
|         } | ||||
|         return nDevices; | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         let connectedDevices = this._applet.get_devices().filter(function(device) { | ||||
|             return device.connected; | ||||
|         }); | ||||
|         let nDevices = connectedDevices.length; | ||||
|         let nDevices = this._getNConnectedDevices(); | ||||
|         let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter; | ||||
|  | ||||
|         let on = nDevices > 0; | ||||
|         this._indicator.visible = on; | ||||
|         this._item.actor.visible = on; | ||||
|         this.menu.setSensitive(sensitive); | ||||
|         this._indicator.visible = nDevices > 0; | ||||
|         this._item.actor.visible = this._proxy.BluetoothHasAirplaneMode && !this._proxy.BluetoothAirplaneMode; | ||||
|  | ||||
|         if (on) | ||||
|         if (nDevices > 0) | ||||
|             this._item.status.text = ngettext("%d Connected Device", "%d Connected Devices", nDevices).format(nDevices); | ||||
|     }, | ||||
|  | ||||
|     _ensureSource: function() { | ||||
|         if (!this._source) { | ||||
|             this._source = new MessageTray.Source(_("Bluetooth"), 'bluetooth-active'); | ||||
|             this._source.policy = new MessageTray.NotificationApplicationPolicy('gnome-bluetooth-panel'); | ||||
|             Main.messageTray.add(this._source); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _authRequest: function(applet, device_path, name, long_name) { | ||||
|         this._ensureSource(); | ||||
|         this._source.notify(new AuthNotification(this._source, this._applet, device_path, name, long_name)); | ||||
|     }, | ||||
|  | ||||
|     _authServiceRequest: function(applet, device_path, name, long_name, uuid) { | ||||
|         this._ensureSource(); | ||||
|         this._source.notify(new AuthServiceNotification(this._source, this._applet, device_path, name, long_name, uuid)); | ||||
|     }, | ||||
|  | ||||
|     _confirmRequest: function(applet, device_path, name, long_name, pin) { | ||||
|         this._ensureSource(); | ||||
|         this._source.notify(new ConfirmNotification(this._source, this._applet, device_path, name, long_name, pin)); | ||||
|     }, | ||||
|  | ||||
|     _pinRequest: function(applet, device_path, name, long_name, numeric) { | ||||
|         this._ensureSource(); | ||||
|         this._source.notify(new PinNotification(this._source, this._applet, device_path, name, long_name, numeric)); | ||||
|     }, | ||||
|  | ||||
|     _cancelRequest: function() { | ||||
|         this._source.destroy(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const AuthNotification = new Lang.Class({ | ||||
|     Name: 'AuthNotification', | ||||
|     Extends: MessageTray.Notification, | ||||
|  | ||||
|     _init: function(source, applet, device_path, name, long_name) { | ||||
|         this.parent(source, | ||||
|                     _("Bluetooth"), | ||||
|                     _("Authorization request from %s").format(name), | ||||
|                     { customContent: true }); | ||||
|         this.setResident(true); | ||||
|  | ||||
|         this._applet = applet; | ||||
|         this._devicePath = device_path; | ||||
|         this.addBody(_("Device %s wants to pair with this computer").format(long_name)); | ||||
|  | ||||
|         this.addAction('allow', _("Allow")); | ||||
|         this.addAction('deny', _("Deny")); | ||||
|  | ||||
|         this.connect('action-invoked', Lang.bind(this, function(self, action) { | ||||
|             if (action == 'allow') | ||||
|                 this._applet.agent_reply_confirm(this._devicePath, true); | ||||
|             else | ||||
|                 this._applet.agent_reply_confirm(this._devicePath, false); | ||||
|             this.destroy(); | ||||
|         })); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const AuthServiceNotification = new Lang.Class({ | ||||
|     Name: 'AuthServiceNotification', | ||||
|     Extends: MessageTray.Notification, | ||||
|  | ||||
|     _init: function(source, applet, device_path, name, long_name, uuid) { | ||||
|         this.parent(source, | ||||
|                     _("Bluetooth"), | ||||
|                     _("Authorization request from %s").format(name), | ||||
|                     { customContent: true }); | ||||
|         this.setResident(true); | ||||
|  | ||||
|         this._applet = applet; | ||||
|         this._devicePath = device_path; | ||||
|         this.addBody(_("Device %s wants access to the service '%s'").format(long_name, uuid)); | ||||
|  | ||||
|         this.addAction('always-grant', _("Always grant access")); | ||||
|         this.addAction('grant', _("Grant this time only")); | ||||
|         this.addAction('reject', _("Reject")); | ||||
|  | ||||
|         this.connect('action-invoked', Lang.bind(this, function(self, action) { | ||||
|             switch (action) { | ||||
|             case 'always-grant': | ||||
|                 this._applet.agent_reply_auth_service(this._devicePath, true, true); | ||||
|                 break; | ||||
|             case 'grant': | ||||
|                 this._applet.agent_reply_auth_service(this._devicePath, true, false); | ||||
|                 break; | ||||
|             case 'reject': | ||||
|             default: | ||||
|                 this._applet.agent_reply_auth_service(this._devicePath, false, false); | ||||
|             } | ||||
|             this.destroy(); | ||||
|         })); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const ConfirmNotification = new Lang.Class({ | ||||
|     Name: 'ConfirmNotification', | ||||
|     Extends: MessageTray.Notification, | ||||
|  | ||||
|     _init: function(source, applet, device_path, name, long_name, pin) { | ||||
|         this.parent(source, | ||||
|                     _("Bluetooth"), | ||||
|                     /* Translators: argument is the device short name */ | ||||
|                     _("Pairing confirmation for %s").format(name), | ||||
|                     { customContent: true }); | ||||
|         this.setResident(true); | ||||
|  | ||||
|         this._applet = applet; | ||||
|         this._devicePath = device_path; | ||||
|         this.addBody(_("Device %s wants to pair with this computer").format(long_name)); | ||||
|         this.addBody(_("Please confirm whether the Passkey '%06d' matches the one on the device.").format(pin)); | ||||
|  | ||||
|         /* Translators: this is the verb, not the noun */ | ||||
|         this.addAction('matches', _("Matches")); | ||||
|         this.addAction('does-not-match', _("Does not match")); | ||||
|  | ||||
|         this.connect('action-invoked', Lang.bind(this, function(self, action) { | ||||
|             if (action == 'matches') | ||||
|                 this._applet.agent_reply_confirm(this._devicePath, true); | ||||
|             else | ||||
|                 this._applet.agent_reply_confirm(this._devicePath, false); | ||||
|             this.destroy(); | ||||
|         })); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const PinNotification = new Lang.Class({ | ||||
|     Name: 'PinNotification', | ||||
|     Extends: MessageTray.Notification, | ||||
|  | ||||
|     _init: function(source, applet, device_path, name, long_name, numeric) { | ||||
|         this.parent(source, | ||||
|                     _("Bluetooth"), | ||||
|                     _("Pairing request for %s").format(name), | ||||
|                     { customContent: true }); | ||||
|         this.setResident(true); | ||||
|  | ||||
|         this._applet = applet; | ||||
|         this._devicePath = device_path; | ||||
|         this._numeric = numeric; | ||||
|         this.addBody(_("Device %s wants to pair with this computer").format(long_name)); | ||||
|         this.addBody(_("Please enter the PIN mentioned on the device.")); | ||||
|  | ||||
|         this._entry = new St.Entry(); | ||||
|         this._entry.connect('key-release-event', Lang.bind(this, function(entry, event) { | ||||
|             let key = event.get_key_symbol(); | ||||
|             if (key == Clutter.KEY_Return) { | ||||
|                 if (this._canActivateOkButton()) | ||||
|                     this._ok(); | ||||
|                 return true; | ||||
|             } else if (key == Clutter.KEY_Escape) { | ||||
|                 this._cancel(); | ||||
|                 return true; | ||||
|             } | ||||
|             return false; | ||||
|         })); | ||||
|         this.addActor(this._entry); | ||||
|  | ||||
|         let okButton = this.addAction(_("OK"), Lang.bind(this, this._ok)); | ||||
|         this.addAction(_("Cancel"), Lang.bind(this, this._cancel)); | ||||
|  | ||||
|         okButton.reactive = this._canActivateOkButton(); | ||||
|         this._entry.clutter_text.connect('text-changed', Lang.bind(this, function() { | ||||
|             okButton.reactive = this._canActivateOkButton(); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     _ok: function() { | ||||
|         if (this._numeric) { | ||||
|             let num = parseInt(this._entry.text, 10); | ||||
|             if (isNaN(num)) { | ||||
|                 // user reply was empty, or was invalid | ||||
|                 // cancel the operation | ||||
|                 num = -1; | ||||
|             } | ||||
|             this._applet.agent_reply_passkey(this._devicePath, num); | ||||
|         } else { | ||||
|             this._applet.agent_reply_pincode(this._devicePath, this._entry.text); | ||||
|         } | ||||
|         this.destroy(); | ||||
|     }, | ||||
|  | ||||
|     _cancel: function() { | ||||
|         if (this._numeric) | ||||
|             this._applet.agent_reply_passkey(this._devicePath, -1); | ||||
|         else | ||||
|             this._applet.agent_reply_pincode(this._devicePath, null); | ||||
|         this.destroy(); | ||||
|             this._item.status.text = _("Not Connected"); | ||||
|     }, | ||||
|  | ||||
|     _canActivateOkButton: function() { | ||||
|         // PINs have a fixed length of 6 | ||||
|         if (this._numeric) | ||||
|             return this._entry.clutter_text.text.length == 6; | ||||
|         else | ||||
|             return true; | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -48,7 +48,7 @@ const Indicator = new Lang.Class({ | ||||
|         this._item.actor.add(icon); | ||||
|         this._item.actor.add(this._slider.actor, { expand: true }); | ||||
|         this._item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) { | ||||
|             this._slider.startDragging(event); | ||||
|             return this._slider.startDragging(event); | ||||
|         })); | ||||
|         this._item.actor.connect('key-press-event', Lang.bind(this, function(actor, event) { | ||||
|             return this._slider.onKeyPressEvent(actor, event); | ||||
|   | ||||
| @@ -292,6 +292,10 @@ const InputSourcePopup = new Lang.Class({ | ||||
|             this._select(this._previous()); | ||||
|         else if (keysym == Clutter.Right) | ||||
|             this._select(this._next()); | ||||
|         else | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _finish : function() { | ||||
| @@ -341,7 +345,7 @@ const InputSourceIndicator = new Lang.Class({ | ||||
|  | ||||
|         this._hbox = new St.BoxLayout({ style_class: 'panel-status-menu-box' }); | ||||
|         this._hbox.add_child(this._container); | ||||
|         this._hbox.add_child(PopupMenu.unicodeArrow(St.Side.BOTTOM)); | ||||
|         this._hbox.add_child(PopupMenu.arrowIcon(St.Side.BOTTOM)); | ||||
|  | ||||
|         this.actor.add_child(this._hbox); | ||||
|         this.actor.add_style_class_name('panel-status-button'); | ||||
|   | ||||
							
								
								
									
										201
									
								
								js/ui/status/location.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										201
									
								
								js/ui/status/location.js
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,201 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gio = imports.gi.Gio; | ||||
| const Lang = imports.lang; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const Shell = imports.gi.Shell; | ||||
|  | ||||
| const LOCATION_SCHEMA = 'org.gnome.shell.location'; | ||||
| const MAX_ACCURACY_LEVEL = 'max-accuracy-level'; | ||||
|  | ||||
| var GeoclueIface = '<node> \ | ||||
|   <interface name="org.freedesktop.GeoClue2.Manager"> \ | ||||
|     <property name="InUse" type="b" access="read"/> \ | ||||
|     <property name="AvailableAccuracyLevel" type="u" access="read"/> \ | ||||
|     <method name="AddAgent"> \ | ||||
|       <arg name="id" type="s" direction="in"/> \ | ||||
|     </method> \ | ||||
|   </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const GeoclueManager = Gio.DBusProxy.makeProxyWrapper(GeoclueIface); | ||||
|  | ||||
| var AgentIface = '<node> \ | ||||
|   <interface name="org.freedesktop.GeoClue2.Agent"> \ | ||||
|     <property name="MaxAccuracyLevel" type="u" access="read"/> \ | ||||
|     <method name="AuthorizeApp"> \ | ||||
|       <arg name="desktop_id" type="s" direction="in"/> \ | ||||
|       <arg name="req_accuracy_level" type="u" direction="in"/> \ | ||||
|       <arg name="authorized" type="b" direction="out"/> \ | ||||
|       <arg name="allowed_accuracy_level" type="u" direction="out"/> \ | ||||
|     </method> \ | ||||
|   </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const Indicator = new Lang.Class({ | ||||
|     Name: 'LocationIndicator', | ||||
|     Extends: PanelMenu.SystemIndicator, | ||||
|  | ||||
|     _init: function() { | ||||
|         this.parent(); | ||||
|  | ||||
|         this._settings = new Gio.Settings({ schema: LOCATION_SCHEMA }); | ||||
|         this._settings.connect('changed::' + MAX_ACCURACY_LEVEL, | ||||
|                                Lang.bind(this, this._onMaxAccuracyLevelChanged)); | ||||
|  | ||||
|         this._indicator = this._addIndicator(); | ||||
|         this._indicator.icon_name = 'find-location-symbolic'; | ||||
|  | ||||
|         this._item = new PopupMenu.PopupSubMenuMenuItem(_("Location"), true); | ||||
|         this._item.icon.icon_name = 'find-location-symbolic'; | ||||
|  | ||||
|         this._agent = Gio.DBusExportedObject.wrapJSObject(AgentIface, this); | ||||
|         this._agent.export(Gio.DBus.system, '/org/freedesktop/GeoClue2/Agent'); | ||||
|  | ||||
|         this._item.status.text = _("Enabled"); | ||||
|         this._onOffAction = this._item.menu.addAction(_("Disable"), Lang.bind(this, this._onOnOffAction)); | ||||
|  | ||||
|         this.menu.addMenuItem(this._item); | ||||
|  | ||||
|         this._watchId = Gio.bus_watch_name(Gio.BusType.SYSTEM, | ||||
|                                            'org.freedesktop.GeoClue2', | ||||
|                                            0, | ||||
|                                            Lang.bind(this, this._connectToGeoclue), | ||||
|                                            Lang.bind(this, this._onGeoclueVanished)); | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._onSessionUpdated)); | ||||
|         this._onSessionUpdated(); | ||||
|         this._onMaxAccuracyLevelChanged(); | ||||
|         this._connectToGeoclue(); | ||||
|     }, | ||||
|  | ||||
|     get MaxAccuracyLevel() { | ||||
|         return this._getMaxAccuracyLevel(); | ||||
|     }, | ||||
|  | ||||
|     // We (and geoclue) have currently no way to reliably identifying apps so | ||||
|     // for now, lets just authorize all apps as long as they provide a valid | ||||
|     // desktop ID. We also ensure they don't get more accuracy than global max. | ||||
|     AuthorizeApp: function(desktop_id, reqAccuracyLevel) { | ||||
|         var appSystem = Shell.AppSystem.get_default(); | ||||
|         var app = appSystem.lookup_app(desktop_id + ".desktop"); | ||||
|         if (app == null) { | ||||
|             return [false, 0]; | ||||
|         } | ||||
|  | ||||
|         let allowedAccuracyLevel = clamp(reqAccuracyLevel, 0, this._getMaxAccuracyLevel()); | ||||
|         return [true, allowedAccuracyLevel]; | ||||
|     }, | ||||
|  | ||||
|     _syncIndicator: function() { | ||||
|         if (this._proxy == null) { | ||||
|             this._indicator.visible = false; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._indicator.visible = this._proxy.InUse; | ||||
|         this._updateMenuLabels(); | ||||
|     }, | ||||
|  | ||||
|     _connectToGeoclue: function() { | ||||
|         if (this._proxy != null || this._connecting) | ||||
|             return false; | ||||
|  | ||||
|         this._connecting = true; | ||||
|         new GeoclueManager(Gio.DBus.system, | ||||
|                            'org.freedesktop.GeoClue2', | ||||
|                            '/org/freedesktop/GeoClue2/Manager', | ||||
|                            Lang.bind(this, this._onProxyReady)); | ||||
|         return true; | ||||
|     }, | ||||
|  | ||||
|     _onProxyReady: function(proxy, error) { | ||||
|         if (error != null) { | ||||
|             log(error.message); | ||||
|             this._connecting = false; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         this._proxy = proxy; | ||||
|         this._propertiesChangedId = this._proxy.connect('g-properties-changed', | ||||
|                                                         Lang.bind(this, this._onGeocluePropsChanged)); | ||||
|  | ||||
|         this._availableAccuracyLevel = this._proxy.AvailableAccuracyLevel; | ||||
|         this._syncIndicator(); | ||||
|  | ||||
|         this._proxy.AddAgentRemote('gnome-shell', Lang.bind(this, this._onAgentRegistered)); | ||||
|     }, | ||||
|  | ||||
|     _onAgentRegistered: function(result, error) { | ||||
|         this._connecting = false; | ||||
|         this._notifyMaxAccuracyLevel(); | ||||
|  | ||||
|         if (error != null) | ||||
|             log(error.message); | ||||
|     }, | ||||
|  | ||||
|     _onGeoclueVanished: function() { | ||||
|         if (this._propertiesChangedId) { | ||||
|             this._proxy.disconnect(this._propertiesChangedId); | ||||
|             this._propertiesChangedId = 0; | ||||
|         } | ||||
|         this._proxy = null; | ||||
|  | ||||
|         this._syncIndicator(); | ||||
|     }, | ||||
|  | ||||
|     _onOnOffAction: function() { | ||||
|         if (this._getMaxAccuracyLevel() == 0) | ||||
|             this._settings.set_enum(MAX_ACCURACY_LEVEL, this._availableAccuracyLevel); | ||||
|         else | ||||
|             this._settings.set_enum(MAX_ACCURACY_LEVEL, 0); | ||||
|     }, | ||||
|  | ||||
|     _onSessionUpdated: function() { | ||||
|         let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter; | ||||
|         this.menu.setSensitive(sensitive); | ||||
|     }, | ||||
|  | ||||
|     _updateMenuLabels: function() { | ||||
|         if (this._getMaxAccuracyLevel() == 0) { | ||||
|             this._item.status.text = _("Disabled"); | ||||
|             this._onOffAction.label.text = _("Enable"); | ||||
|         } else { | ||||
|             this._item.status.text = this._indicator.visible ? _("In Use") : _("Enabled"); | ||||
|             this._onOffAction.label.text = _("Disable"); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _onMaxAccuracyLevelChanged: function() { | ||||
|         this._updateMenuLabels(); | ||||
|  | ||||
|         // Gotta ensure geoclue is up and we are registered as agent to it | ||||
|         // before we emit the notify for this property change. | ||||
|         if (!this._connectToGeoclue()) | ||||
|             this._notifyMaxAccuracyLevel(); | ||||
|     }, | ||||
|  | ||||
|     _getMaxAccuracyLevel: function() { | ||||
|         return this._settings.get_enum(MAX_ACCURACY_LEVEL); | ||||
|     }, | ||||
|  | ||||
|     _notifyMaxAccuracyLevel: function() { | ||||
|         let variant = new GLib.Variant('u', this._getMaxAccuracyLevel()); | ||||
|         this._agent.emit_property_changed('MaxAccuracyLevel', variant); | ||||
|     }, | ||||
|  | ||||
|     _onGeocluePropsChanged: function(proxy, properties) { | ||||
|         let unpacked = properties.deep_unpack(); | ||||
|         if ("InUse" in unpacked) | ||||
|             this._syncIndicator(); | ||||
|         if ("AvailableAccuracyLevel" in unpacked) | ||||
|             this._availableAccuracyLevel = this._proxy.AvailableAccuracyLevel; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| function clamp(value, min, max) { | ||||
|     return Math.max(min, Math.min(max, value)); | ||||
| } | ||||
| @@ -9,19 +9,22 @@ const NetworkManager = imports.gi.NetworkManager; | ||||
| const NMClient = imports.gi.NMClient; | ||||
| const NMGtk = imports.gi.NMGtk; | ||||
| const Signals = imports.signals; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
|  | ||||
| const Hash = imports.misc.hash; | ||||
| const Animation = imports.ui.animation; | ||||
| const Main = imports.ui.main; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
| const MessageTray = imports.ui.messageTray; | ||||
| const ModalDialog = imports.ui.modalDialog; | ||||
| const ModemManager = imports.misc.modemManager; | ||||
| const Rfkill = imports.ui.status.rfkill; | ||||
| const Util = imports.misc.util; | ||||
|  | ||||
| const NMConnectionCategory = { | ||||
|     INVALID: 'invalid', | ||||
|     WIRED: 'wired', | ||||
|     WIRELESS: 'wireless', | ||||
|     WWAN: 'wwan', | ||||
|     VPN: 'vpn' | ||||
| @@ -102,18 +105,34 @@ const NMConnectionItem = new Lang.Class({ | ||||
|         this._activeConnection = null; | ||||
|         this._activeConnectionChangedId = 0; | ||||
|  | ||||
|         this._buildUI(); | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     _buildUI: function() { | ||||
|         this.labelItem = new PopupMenu.PopupMenuItem(''); | ||||
|         this.labelItem.connect('activate', Lang.bind(this, this._toggle)); | ||||
|  | ||||
|         this.switchItem = new PopupMenu.PopupSwitchMenuItem(connection.get_id(), false); | ||||
|         this.switchItem.connect('toggled', Lang.bind(this, this._toggle)); | ||||
|  | ||||
|         this._sync(); | ||||
|         this.radioItem = new PopupMenu.PopupMenuItem(this._connection.get_id(), false); | ||||
|         this.radioItem.connect('activate', Lang.bind(this, this._activate)); | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
|         this.labelItem.destroy(); | ||||
|         this.switchItem.destroy(); | ||||
|         this.radioItem.destroy(); | ||||
|     }, | ||||
|  | ||||
|     updateForConnection: function(connection) { | ||||
|         // connection should always be the same object | ||||
|         // (and object path) as this._connection, but | ||||
|         // this can be false if NetworkManager was restarted | ||||
|         // and picked up connections in a different order | ||||
|         // Just to be safe, we set it here again | ||||
|  | ||||
|         this._connection = connection; | ||||
|         this.radioItem.label.text = connection.get_id(); | ||||
|         this._sync(); | ||||
|         this.emit('name-changed'); | ||||
|     }, | ||||
|  | ||||
|     getName: function() { | ||||
| @@ -129,9 +148,8 @@ const NMConnectionItem = new Lang.Class({ | ||||
|  | ||||
|     _sync: function() { | ||||
|         let isActive = this.isActive(); | ||||
|         this.labelItem.label.text = isActive ? _("Turn Off") : _("Connect"); | ||||
|         this.switchItem.setToggleState(isActive); | ||||
|         this.switchItem.setStatus(this._getStatus()); | ||||
|         this.labelItem.label.text = isActive ? _("Turn Off") : this._section.getConnectLabel(); | ||||
|         this.radioItem.setOrnament(isActive ? PopupMenu.Ornament.DOT : PopupMenu.Ornament.NONE); | ||||
|         this.emit('icon-changed'); | ||||
|     }, | ||||
|  | ||||
| @@ -144,8 +162,11 @@ const NMConnectionItem = new Lang.Class({ | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     _getStatus: function() { | ||||
|         return null; | ||||
|     _activate: function() { | ||||
|         if (this._activeConnection == null) | ||||
|             this._section.activateConnection(this._connection); | ||||
|  | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     _connectionStateChanged: function(ac, newstate, reason) { | ||||
| @@ -176,15 +197,15 @@ const NMConnectionSection = new Lang.Class({ | ||||
|     _init: function(client) { | ||||
|         this._client = client; | ||||
|  | ||||
|         this._connectionItems = new Hash.Map(); | ||||
|         this._connectionItems = new Map(); | ||||
|         this._connections = []; | ||||
|  | ||||
|         this._labelSection = new PopupMenu.PopupMenuSection(); | ||||
|         this._switchSection = new PopupMenu.PopupMenuSection(); | ||||
|         this._radioSection = new PopupMenu.PopupMenuSection(); | ||||
|  | ||||
|         this.item = new PopupMenu.PopupSubMenuMenuItem('', true); | ||||
|         this.item.menu.addMenuItem(this._labelSection); | ||||
|         this.item.menu.addMenuItem(this._switchSection); | ||||
|         this.item.menu.addMenuItem(this._radioSection); | ||||
|  | ||||
|         this.connect('icon-changed', Lang.bind(this, this._sync)); | ||||
|     }, | ||||
| @@ -194,9 +215,9 @@ const NMConnectionSection = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         let nItems = this._connectionItems.size(); | ||||
|         let nItems = this._connectionItems.size; | ||||
|  | ||||
|         this._switchSection.actor.visible = (nItems > 1); | ||||
|         this._radioSection.actor.visible = (nItems > 1); | ||||
|         this._labelSection.actor.visible = (nItems == 1); | ||||
|  | ||||
|         this.item.status.text = this._getStatus(); | ||||
| @@ -211,19 +232,12 @@ const NMConnectionSection = new Lang.Class({ | ||||
|             this.item.label.text = ''; | ||||
|     }, | ||||
|  | ||||
|     _getStatus: function() { | ||||
|         let values = this._connectionItems.values(); | ||||
|         for (let i = 0; i < values.length; i++) { | ||||
|             let item = values[i]; | ||||
|             if (item.isActive()) | ||||
|                 return item.getName(); | ||||
|         } | ||||
|  | ||||
|         return _("Off"); | ||||
|     _getMenuIcon: function() { | ||||
|         return this.getIndicatorIcon(); | ||||
|     }, | ||||
|  | ||||
|     _hasConnection: function(connection) { | ||||
|         return this._connectionItems.has(connection.get_uuid()); | ||||
|     getConnectLabel: function() { | ||||
|         return _("Connect"); | ||||
|     }, | ||||
|  | ||||
|     _connectionValid: function(connection) { | ||||
| @@ -231,10 +245,7 @@ const NMConnectionSection = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _connectionSortFunction: function(one, two) { | ||||
|         if (one._timestamp == two._timestamp) | ||||
|             return GLib.utf8_collate(one.get_id(), two.get_id()); | ||||
|  | ||||
|         return two._timestamp - one._timestamp; | ||||
|         return GLib.utf8_collate(one.get_id(), two.get_id()); | ||||
|     }, | ||||
|  | ||||
|     _makeConnectionItem: function(connection) { | ||||
| @@ -245,10 +256,20 @@ const NMConnectionSection = new Lang.Class({ | ||||
|         if (!this._connectionValid(connection)) | ||||
|             return; | ||||
|  | ||||
|         if (this._hasConnection(connection)) | ||||
|             return; | ||||
|         // This function is called everytime connection is added or updated | ||||
|         // In the usual case, we already added this connection and UUID | ||||
|         // didn't change. So we need to check if we already have an item, | ||||
|         // and update it for properties in the connection that changed | ||||
|         // (the only one we care about is the name) | ||||
|         // But it's also possible we didn't know about this connection | ||||
|         // (eg, during coldplug, or because it was updated and suddenly | ||||
|         // it's valid for this device), in which case we add a new item | ||||
|  | ||||
|         this._addConnection(connection); | ||||
|         let item = this._connectionItems.get(connection.get_uuid()); | ||||
|         if (item) | ||||
|             item.updateForConnection(connection); | ||||
|         else | ||||
|             this._addConnection(connection); | ||||
|     }, | ||||
|  | ||||
|     _addConnection: function(connection) { | ||||
| @@ -262,17 +283,23 @@ const NMConnectionSection = new Lang.Class({ | ||||
|         item.connect('activation-failed', Lang.bind(this, function(item, reason) { | ||||
|             this.emit('activation-failed', reason); | ||||
|         })); | ||||
|         item.connect('name-changed', Lang.bind(this, this._sync)); | ||||
|  | ||||
|         let pos = Util.insertSorted(this._connections, connection, Lang.bind(this, this._connectionSortFunction)); | ||||
|         this._labelSection.addMenuItem(item.labelItem, pos); | ||||
|         this._switchSection.addMenuItem(item.switchItem, pos); | ||||
|         this._radioSection.addMenuItem(item.radioItem, pos); | ||||
|         this._connectionItems.set(connection.get_uuid(), item); | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     removeConnection: function(connection) { | ||||
|         this._connectionItems.get(connection.get_uuid()).destroy(); | ||||
|         this._connectionItems.delete(connection.get_uuid()); | ||||
|         let uuid = connection.get_uuid(); | ||||
|         let item = this._connectionItems.get(uuid); | ||||
|         if (item == undefined) | ||||
|             return; | ||||
|  | ||||
|         item.destroy(); | ||||
|         this._connectionItems.delete(uuid); | ||||
|  | ||||
|         let pos = this._connections.indexOf(connection); | ||||
|         this._connections.splice(pos, 1); | ||||
| @@ -293,11 +320,24 @@ const NMConnectionDevice = new Lang.Class({ | ||||
|         this._settings = settings; | ||||
|  | ||||
|         this._autoConnectItem = this.item.menu.addAction(_("Connect"), Lang.bind(this, this._autoConnect)); | ||||
|         this._deactivateItem = this._radioSection.addAction(_("Turn Off"), Lang.bind(this, this.deactivateConnection)); | ||||
|  | ||||
|         this._stateChangedId = this._device.connect('state-changed', Lang.bind(this, this._deviceStateChanged)); | ||||
|         this._activeConnectionChangedId = this._device.connect('notify::active-connection', Lang.bind(this, this._activeConnectionChanged)); | ||||
|     }, | ||||
|  | ||||
|     _canReachInternet: function() { | ||||
|         if (this._client.primary_connection != this._device.active_connection) | ||||
|             return true; | ||||
|  | ||||
|         return this._client.connectivity == NetworkManager.ConnectivityState.FULL; | ||||
|     }, | ||||
|  | ||||
|     _autoConnect: function() { | ||||
|         let connection = new NetworkManager.Connection(); | ||||
|         this._client.add_and_activate_connection(connection, this._device, null, null); | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
|         if (this._stateChangedId) { | ||||
|             GObject.Object.prototype.disconnect.call(this._device, this._stateChangedId); | ||||
| @@ -305,7 +345,7 @@ const NMConnectionDevice = new Lang.Class({ | ||||
|         } | ||||
|         if (this._activeConnectionChangedId) { | ||||
|             GObject.Object.prototype.disconnect.call(this._device, this._activeConnectionChangedId); | ||||
|             this._stateChangedId = 0; | ||||
|             this._activeConnectionChangedId = 0; | ||||
|         } | ||||
|  | ||||
|         this.parent(); | ||||
| @@ -365,8 +405,9 @@ const NMConnectionDevice = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         let nItems = this._connectionItems.size(); | ||||
|         let nItems = this._connectionItems.size; | ||||
|         this._autoConnectItem.actor.visible = (nItems == 0); | ||||
|         this._deactivateItem.actor.visible = this._device.state > NetworkManager.DeviceState.DISCONNECTED; | ||||
|         this.parent(); | ||||
|     }, | ||||
|  | ||||
| @@ -378,7 +419,7 @@ const NMConnectionDevice = new Lang.Class({ | ||||
|         case NetworkManager.DeviceState.DISCONNECTED: | ||||
|             return _("Off"); | ||||
|         case NetworkManager.DeviceState.ACTIVATED: | ||||
|             return this.parent(); | ||||
|             return _("Connected"); | ||||
|         case NetworkManager.DeviceState.UNMANAGED: | ||||
|             /* Translators: this is for network devices that are physically present but are not | ||||
|                under NetworkManager's control (and thus cannot be used in the menu) */ | ||||
| @@ -415,6 +456,48 @@ const NMConnectionDevice = new Lang.Class({ | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| const NMDeviceWired = new Lang.Class({ | ||||
|     Name: 'NMDeviceWired', | ||||
|     Extends: NMConnectionDevice, | ||||
|     category: NMConnectionCategory.WIRED, | ||||
|  | ||||
|     _init: function(client, device, settings) { | ||||
|         this.parent(client, device, settings); | ||||
|  | ||||
|         this.item.menu.addMenuItem(createSettingsAction(_("Wired Settings"), device)); | ||||
|     }, | ||||
|  | ||||
|     _hasCarrier: function() { | ||||
|         if (this._device instanceof NMClient.DeviceEthernet) | ||||
|             return this._device.carrier; | ||||
|         else | ||||
|             return true; | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         this.item.actor.visible = this._hasCarrier(); | ||||
|         this.parent(); | ||||
|     }, | ||||
|  | ||||
|     getIndicatorIcon: function() { | ||||
|         if (this._device.active_connection) { | ||||
|             let state = this._device.active_connection.state; | ||||
|  | ||||
|             if (state == NetworkManager.ActiveConnectionState.ACTIVATING) { | ||||
|                 return 'network-wired-acquiring-symbolic'; | ||||
|             } else if (state == NetworkManager.ActiveConnectionState.ACTIVATED) { | ||||
|                 if (this._canReachInternet()) | ||||
|                     return 'network-wired-symbolic'; | ||||
|                 else | ||||
|                     return 'network-wired-no-route-symbolic'; | ||||
|             } else { | ||||
|                 return 'network-wired-disconnected-symbolic'; | ||||
|             } | ||||
|         } else | ||||
|             return 'network-wired-disconnected-symbolic'; | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const NMDeviceModem = new Lang.Class({ | ||||
|     Name: 'NMDeviceModem', | ||||
|     Extends: NMConnectionDevice, | ||||
| @@ -477,28 +560,20 @@ const NMDeviceModem = new Lang.Class({ | ||||
|             return this.parent(); | ||||
|     }, | ||||
|  | ||||
|     _getMenuIcon: function() { | ||||
|         if (this._device.active_connection) | ||||
|             return this.getIndicatorIcon(); | ||||
|         else | ||||
|     getIndicatorIcon: function() { | ||||
|         if (this._device.active_connection) { | ||||
|             if (this._device.active_connection.state == NetworkManager.ActiveConnectionState.ACTIVATING) | ||||
|                 return 'network-cellular-acquiring-symbolic'; | ||||
|  | ||||
|             return this._getSignalIcon(); | ||||
|         } else { | ||||
|             return 'network-cellular-signal-none-symbolic'; | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _getSignalIcon: function() { | ||||
|         return 'network-cellular-signal-' + signalToIcon(this._mobileDevice.signal_quality) + '-symbolic'; | ||||
|     }, | ||||
|  | ||||
|     getIndicatorIcon: function() { | ||||
|         if (this._device.active_connection.state == NetworkManager.ActiveConnectionState.ACTIVATING) | ||||
|             return 'network-cellular-acquiring-symbolic'; | ||||
|  | ||||
|         if (!this._mobileDevice) { | ||||
|             // this can happen for bluetooth in PAN mode | ||||
|             return 'network-cellular-connected-symbolic'; | ||||
|         } | ||||
|  | ||||
|         return this._getSignalIcon(); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const NMDeviceBluetooth = new Lang.Class({ | ||||
| @@ -512,33 +587,26 @@ const NMDeviceBluetooth = new Lang.Class({ | ||||
|         this.item.menu.addMenuItem(createSettingsAction(_("Mobile Broadband Settings"), device)); | ||||
|     }, | ||||
|  | ||||
|     _autoConnect: function() { | ||||
|         // FIXME: DUN devices are configured like modems, so | ||||
|         // We need to spawn the mobile wizard | ||||
|         // but the network panel doesn't support bluetooth at the moment | ||||
|         // so we just create an empty connection and hope | ||||
|         // that this phone supports PAN | ||||
|  | ||||
|         let connection = new NetworkManager.Connection(); | ||||
|         this._client.add_and_activate_connection(connection, this._device, null, null); | ||||
|         return true; | ||||
|     _getDescription: function() { | ||||
|         return this._device.name; | ||||
|     }, | ||||
|  | ||||
|     _getMenuIcon: function() { | ||||
|         if (this._device.active_connection) | ||||
|             return this.getIndicatorIcon(); | ||||
|         else | ||||
|             return 'network-cellular-signal-none-symbolic'; | ||||
|     getConnectLabel: function() { | ||||
|         return _("Use as Internet connection"); | ||||
|     }, | ||||
|  | ||||
|     getIndicatorIcon: function() { | ||||
|         let state = this._device.active_connection.state; | ||||
|         if (state == NetworkManager.ActiveConnectionState.ACTIVATING) | ||||
|             return 'network-cellular-acquiring-symbolic'; | ||||
|         else if (state == NetworkManager.ActiveConnectionState.ACTIVATED) | ||||
|             return 'network-cellular-connected-symbolic'; | ||||
|         else | ||||
|         if (this._device.active_connection) { | ||||
|             let state = this._device.active_connection.state; | ||||
|             if (state == NetworkManager.ActiveConnectionState.ACTIVATING) | ||||
|                 return 'network-cellular-acquiring-symbolic'; | ||||
|             else if (state == NetworkManager.ActiveConnectionState.ACTIVATED) | ||||
|                 return 'network-cellular-connected-symbolic'; | ||||
|             else | ||||
|                 return 'network-cellular-signal-none-symbolic'; | ||||
|         } else { | ||||
|             return 'network-cellular-signal-none-symbolic'; | ||||
|         } | ||||
|     } | ||||
| }); | ||||
|  | ||||
| @@ -617,6 +685,13 @@ const NMWirelessDialog = new Lang.Class({ | ||||
|         this._client = client; | ||||
|         this._device = device; | ||||
|  | ||||
|         this._wirelessEnabledChangedId = this._client.connect('notify::wireless-enabled', | ||||
|                                                               Lang.bind(this, this._syncView)); | ||||
|  | ||||
|         this._rfkill = Rfkill.getRfkillManager(); | ||||
|         this._airplaneModeChangedId = this._rfkill.connect('airplane-mode-changed', | ||||
|                                                            Lang.bind(this, this._syncView)); | ||||
|  | ||||
|         this._networks = []; | ||||
|         this._buildLayout(); | ||||
|  | ||||
| @@ -638,6 +713,12 @@ const NMWirelessDialog = new Lang.Class({ | ||||
|         this._selectedNetwork = null; | ||||
|         this._activeApChanged(); | ||||
|         this._updateSensitivity(); | ||||
|         this._syncView(); | ||||
|  | ||||
|         if (accessPoints.length == 0) { | ||||
|             /* If there are no visible access points, request a scan */ | ||||
|             this._device.request_scan_simple(null); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
| @@ -653,6 +734,14 @@ const NMWirelessDialog = new Lang.Class({ | ||||
|             GObject.Object.prototype.disconnect.call(this._device, this._activeApChangedId); | ||||
|             this._activeApChangedId = 0; | ||||
|         } | ||||
|         if (this._wirelessEnabledChangedId) { | ||||
|             this._client.disconnect(this._wirelessEnabledChangedId); | ||||
|             this._wirelessEnabledChangedId = 0; | ||||
|         } | ||||
|         if (this._airplaneModeChangedId) { | ||||
|             this._rfkill.disconnect(this._airplaneModeChangedId); | ||||
|             this._airplaneModeChangedId = 0; | ||||
|         } | ||||
|  | ||||
|         this.parent(); | ||||
|     }, | ||||
| @@ -674,13 +763,44 @@ const NMWirelessDialog = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _updateSensitivity: function() { | ||||
|         let connectSensitive = this._selectedNetwork && (this._selectedNetwork != this._activeNetwork); | ||||
|         let connectSensitive = this._client.wireless_enabled && this._selectedNetwork && (this._selectedNetwork != this._activeNetwork); | ||||
|         this._connectButton.reactive = connectSensitive; | ||||
|         this._connectButton.can_focus = connectSensitive; | ||||
|     }, | ||||
|  | ||||
|     _updateVisibility: function() { | ||||
|         this._noNetworksLabel.visible = (this._networks.length == 0); | ||||
|     _syncView: function() { | ||||
|         if (this._rfkill.airplaneMode) { | ||||
|             this._airplaneBox.show(); | ||||
|  | ||||
|             this._airplaneIcon.icon_name = 'airplane-mode-symbolic'; | ||||
|             this._airplaneHeadline.text = _("Airplane Mode is On"); | ||||
|             this._airplaneText.text = _("Wi-Fi is disabled when airplane mode is on."); | ||||
|             this._airplaneButton.label = _("Turn Off Airplane Mode"); | ||||
|  | ||||
|             this._airplaneButton.visible = !this._rfkill.hwAirplaneMode; | ||||
|             this._airplaneInactive.visible = this._rfkill.hwAirplaneMode; | ||||
|             this._noNetworksBox.hide(); | ||||
|         } else if (!this._client.wireless_enabled) { | ||||
|             this._airplaneBox.show(); | ||||
|  | ||||
|             this._airplaneIcon.icon_name = 'dialog-information-symbolic'; | ||||
|             this._airplaneHeadline.text = _("Wi-Fi is Off"); | ||||
|             this._airplaneText.text = _("Wi-Fi needs to be turned on in order to connect to a network."); | ||||
|             this._airplaneButton.label = _("Turn On Wi-Fi"); | ||||
|  | ||||
|             this._airplaneButton.show(); | ||||
|             this._airplaneInactive.hide(); | ||||
|             this._noNetworksBox.hide(); | ||||
|         } else { | ||||
|             this._airplaneBox.hide(); | ||||
|  | ||||
|             this._noNetworksBox.visible = (this._networks.length == 0); | ||||
|         } | ||||
|  | ||||
|         if (this._noNetworksBox.visible) | ||||
|             this._noNetworksSpinner.play(); | ||||
|         else | ||||
|             this._noNetworksSpinner.stop(); | ||||
|     }, | ||||
|  | ||||
|     _buildLayout: function() { | ||||
| @@ -714,11 +834,43 @@ const NMWirelessDialog = new Lang.Class({ | ||||
|         this._scrollView.add_actor(this._itemBox); | ||||
|         this._stack.add_child(this._scrollView); | ||||
|  | ||||
|         this._noNetworksLabel = new St.Label({ style_class: 'no-networks-label', | ||||
|         this._noNetworksBox = new St.BoxLayout({ vertical: true, | ||||
|                                                  style_class: 'no-networks-box', | ||||
|                                                  x_align: Clutter.ActorAlign.CENTER, | ||||
|                                                  y_align: Clutter.ActorAlign.CENTER }); | ||||
|  | ||||
|         this._noNetworksSpinner = new Animation.AnimatedIcon(global.datadir + '/theme/process-working.svg', 24, 24); | ||||
|         this._noNetworksBox.add_actor(this._noNetworksSpinner.actor); | ||||
|         this._noNetworksBox.add_actor(new St.Label({ style_class: 'no-networks-label', | ||||
|                                                      text: _("No Networks") })); | ||||
|         this._stack.add_child(this._noNetworksBox); | ||||
|  | ||||
|         this._airplaneBox = new St.BoxLayout({ vertical: true, | ||||
|                                                style_class: 'nm-dialog-airplane-box', | ||||
|                                                x_align: Clutter.ActorAlign.CENTER, | ||||
|                                                y_align: Clutter.ActorAlign.CENTER, | ||||
|                                                text: _("No Networks") }); | ||||
|         this._stack.add_child(this._noNetworksLabel); | ||||
|                                                y_align: Clutter.ActorAlign.CENTER }); | ||||
|         this._airplaneIcon = new St.Icon({ icon_size: 48 }); | ||||
|         this._airplaneHeadline = new St.Label({ style_class: 'nm-dialog-airplane-headline' }); | ||||
|         this._airplaneText = new St.Label({ style_class: 'nm-dialog-airplane-text' }); | ||||
|  | ||||
|         let airplaneSubStack = new St.Widget({ layout_manager: new Clutter.BinLayout }); | ||||
|         this._airplaneButton = new St.Button({ style_class: 'modal-dialog-button' }); | ||||
|         this._airplaneButton.connect('clicked', Lang.bind(this, function() { | ||||
|             if (this._rfkill.airplaneMode) | ||||
|                 this._rfkill.airplaneMode = false; | ||||
|             else | ||||
|                 this._client.wireless_enabled = true; | ||||
|         })); | ||||
|         airplaneSubStack.add_actor(this._airplaneButton); | ||||
|         this._airplaneInactive = new St.Label({ style_class: 'nm-dialog-airplane-text', | ||||
|                                                 text: _("Use hardware switch to turn off") }); | ||||
|         airplaneSubStack.add_actor(this._airplaneInactive); | ||||
|  | ||||
|         this._airplaneBox.add(this._airplaneIcon, { x_align: St.Align.MIDDLE }); | ||||
|         this._airplaneBox.add(this._airplaneHeadline, { x_align: St.Align.MIDDLE }); | ||||
|         this._airplaneBox.add(this._airplaneText, { x_align: St.Align.MIDDLE }); | ||||
|         this._airplaneBox.add(airplaneSubStack, { x_align: St.Align.MIDDLE }); | ||||
|         this._stack.add_child(this._airplaneBox); | ||||
|  | ||||
|         this.contentLayout.add(this._stack, { expand: true }); | ||||
|  | ||||
| @@ -735,16 +887,11 @@ const NMWirelessDialog = new Lang.Class({ | ||||
|  | ||||
|     _connect: function() { | ||||
|         let network = this._selectedNetwork; | ||||
|         let accessPoints = network.accessPoints; | ||||
|         if (network.connections.length > 0) { | ||||
|             let connection = network.connections[0]; | ||||
|             for (let i = 0; i < accessPoints.length; i++) { | ||||
|                 if (accessPoints[i].connection_valid(connection)) { | ||||
|                     this._client.activate_connection(connection, this._device, accessPoints[i].dbus_path, null); | ||||
|                     break; | ||||
|                 } | ||||
|             } | ||||
|             this._client.activate_connection(connection, this._device, null, null); | ||||
|         } else { | ||||
|             let accessPoints = network.accessPoints; | ||||
|             if ((accessPoints[0]._secType == NMAccessPointSecurity.WPA2_ENT) | ||||
|                 || (accessPoints[0]._secType == NMAccessPointSecurity.WPA_ENT)) { | ||||
|                 // 802.1x-enabled APs require further configuration, so they're | ||||
| @@ -916,7 +1063,7 @@ const NMWirelessDialog = new Lang.Class({ | ||||
|             this._itemBox.insert_child_at_index(network.item.actor, newPos); | ||||
|         } | ||||
|  | ||||
|         this._updateVisibility(); | ||||
|         this._syncView(); | ||||
|     }, | ||||
|  | ||||
|     _accessPointRemoved: function(device, accessPoint) { | ||||
| @@ -931,14 +1078,14 @@ const NMWirelessDialog = new Lang.Class({ | ||||
|         network.accessPoints.splice(res.ap, 1); | ||||
|  | ||||
|         if (network.accessPoints.length == 0) { | ||||
|             network.item.destroy(); | ||||
|             network.item.actor.destroy(); | ||||
|             this._networks.splice(res.network, 1); | ||||
|         } else { | ||||
|             network.item.updateBestAP(network.accessPoints[0]); | ||||
|             this._resortItems(); | ||||
|         } | ||||
|  | ||||
|         this._updateVisibility(); | ||||
|         this._syncView(); | ||||
|     }, | ||||
|  | ||||
|     _resortItems: function() { | ||||
| @@ -1023,6 +1170,10 @@ const NMDeviceWireless = new Lang.Class({ | ||||
|             this._client.disconnect(this._wirelessHwEnabledChangedId); | ||||
|             this._wirelessHwEnabledChangedId = 0; | ||||
|         } | ||||
|         if (this._dialog) { | ||||
|             this._dialog.destroy(); | ||||
|             this._dialog = null; | ||||
|         } | ||||
|  | ||||
|         this.item.destroy(); | ||||
|     }, | ||||
| @@ -1096,7 +1247,12 @@ const NMDeviceWireless = new Lang.Class({ | ||||
|     _getStatus: function() { | ||||
|         let ap = this._device.active_access_point; | ||||
|  | ||||
|         if (ap) | ||||
|         if (this._isHotSpotMaster()) | ||||
|             return _("Hotspot Active"); | ||||
|         else if (this._device.state >= NetworkManager.DeviceState.PREPARE && | ||||
|                  this._device.state < NetworkManager.DeviceState.ACTIVATED) | ||||
|             return _("Connecting"); | ||||
|         else if (ap) | ||||
|             return ssidToLabel(ap.get_ssid()); | ||||
|         else if (!this._client.wireless_hardware_enabled) | ||||
|             return _("Hardware Disabled"); | ||||
| @@ -1115,20 +1271,52 @@ const NMDeviceWireless = new Lang.Class({ | ||||
|             return 'network-wireless-signal-none-symbolic'; | ||||
|     }, | ||||
|  | ||||
|     _canReachInternet: function() { | ||||
|         if (this._client.primary_connection != this._device.active_connection) | ||||
|             return true; | ||||
|  | ||||
|         return this._client.connectivity == NetworkManager.ConnectivityState.FULL; | ||||
|     }, | ||||
|  | ||||
|     _isHotSpotMaster: function() { | ||||
|         if (!this._device.active_connection) | ||||
|             return false; | ||||
|  | ||||
|         let connection = this._settings.get_connection_by_path(this._device.active_connection.connection); | ||||
|         if (!connection) | ||||
|             return false; | ||||
|  | ||||
|         let ip4config = connection.get_setting_ip4_config(); | ||||
|         if (!ip4config) | ||||
|             return false; | ||||
|  | ||||
|         return ip4config.get_method() == NetworkManager.SETTING_IP4_CONFIG_METHOD_SHARED; | ||||
|     }, | ||||
|  | ||||
|     getIndicatorIcon: function() { | ||||
|         if (this._device.state >= NetworkManager.DeviceState.PREPARE && | ||||
|             this._device.state < NetworkManager.DeviceState.ACTIVATED) | ||||
|         if (this._device.state < NetworkManager.DeviceState.PREPARE) | ||||
|             return 'network-wireless-disconnected-symbolic'; | ||||
|         if (this._device.state < NetworkManager.DeviceState.ACTIVATED) | ||||
|             return 'network-wireless-acquiring-symbolic'; | ||||
|  | ||||
|         if (this._isHotSpotMaster()) | ||||
|             return 'network-wireless-hotspot-symbolic'; | ||||
|  | ||||
|         let ap = this._device.active_access_point; | ||||
|         if (!ap) { | ||||
|             if (this._device.mode != NM80211Mode.ADHOC) | ||||
|                 log('An active wireless connection, in infrastructure mode, involves no access point?'); | ||||
|  | ||||
|             return 'network-wireless-connected-symbolic'; | ||||
|             if (this._canReachInternet()) | ||||
|                 return 'network-wireless-connected-symbolic'; | ||||
|             else | ||||
|                 return 'network-wireless-no-route-symbolic'; | ||||
|         } | ||||
|  | ||||
|         return 'network-wireless-signal-' + signalToIcon(ap.strength) + '-symbolic'; | ||||
|         if (this._canReachInternet()) | ||||
|             return 'network-wireless-signal-' + signalToIcon(ap.strength) + '-symbolic'; | ||||
|         else | ||||
|             return 'network-wireless-no-route-symbolic'; | ||||
|     }, | ||||
| }); | ||||
| Signals.addSignalMethods(NMDeviceWireless.prototype); | ||||
| @@ -1144,6 +1332,22 @@ const NMVPNConnectionItem = new Lang.Class({ | ||||
|         return this._activeConnection.vpn_state != NetworkManager.VPNConnectionState.DISCONNECTED; | ||||
|     }, | ||||
|  | ||||
|     _buildUI: function() { | ||||
|         this.labelItem = new PopupMenu.PopupMenuItem(''); | ||||
|         this.labelItem.connect('activate', Lang.bind(this, this._toggle)); | ||||
|  | ||||
|         this.radioItem = new PopupMenu.PopupSwitchMenuItem(this._connection.get_id(), false); | ||||
|         this.radioItem.connect('toggled', Lang.bind(this, this._toggle)); | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         let isActive = this.isActive(); | ||||
|         this.labelItem.label.text = isActive ? _("Turn Off") : this._section.getConnectLabel(); | ||||
|         this.radioItem.setToggleState(isActive); | ||||
|         this.radioItem.setStatus(this._getStatus()); | ||||
|         this.emit('icon-changed'); | ||||
|     }, | ||||
|  | ||||
|     _getStatus: function() { | ||||
|         if (this._activeConnection == null) | ||||
|             return null; | ||||
| @@ -1213,19 +1417,53 @@ const NMVPNSection = new Lang.Class({ | ||||
|  | ||||
|     _init: function(client) { | ||||
|         this.parent(client); | ||||
|  | ||||
|         this._vpnSettings = new PopupMenu.PopupMenuItem(''); | ||||
|         this.item.menu.addMenuItem(this._vpnSettings); | ||||
|         this._vpnSettings.connect('activate', Lang.bind(this, this._onSettingsActivate)); | ||||
|  | ||||
|         this._sync(); | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         let nItems = this._connectionItems.size(); | ||||
|         let nItems = this._connectionItems.size; | ||||
|         this.item.actor.visible = (nItems > 0); | ||||
|  | ||||
|         if (nItems > 1) | ||||
|             this._vpnSettings.label.text = _("Network Settings"); | ||||
|         else | ||||
|             this._vpnSettings.label.text = _("VPN Settings"); | ||||
|  | ||||
|         this.parent(); | ||||
|     }, | ||||
|  | ||||
|     _onSettingsActivate: function() { | ||||
|         let nItems = this._connectionItems.size; | ||||
|         if (nItems > 1) { | ||||
|             let appSys = Shell.AppSystem.get_default(); | ||||
|             let app = appSys.lookup_app('gnome-network-panel.desktop'); | ||||
|             app.launch(0, -1); | ||||
|         } else { | ||||
|             let connection = this._connections[0]; | ||||
|             Util.spawnApp(['gnome-control-center', 'network', 'show-device', | ||||
|                            connection.get_path()]); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _getDescription: function() { | ||||
|         return _("VPN"); | ||||
|     }, | ||||
|  | ||||
|     _getStatus: function() { | ||||
|         let values = this._connectionItems.values(); | ||||
|         for (let item of values) { | ||||
|             if (item.isActive()) | ||||
|                 return item.getName(); | ||||
|         } | ||||
|  | ||||
|         return _("Off"); | ||||
|     }, | ||||
|  | ||||
|     _getMenuIcon: function() { | ||||
|         return this.getIndicatorIcon() || 'network-vpn-symbolic'; | ||||
|     }, | ||||
| @@ -1239,9 +1477,10 @@ const NMVPNSection = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     setActiveConnections: function(vpnConnections) { | ||||
|         this._connectionItems.values().forEach(function(item) { | ||||
|         let connections = this._connectionItems.values(); | ||||
|         for (let item of connections) { | ||||
|             item.setActiveConnection(null); | ||||
|         }); | ||||
|         } | ||||
|         vpnConnections.forEach(Lang.bind(this, function(a) { | ||||
|             let item = this._connectionItems.get(a._connection.get_uuid()); | ||||
|             item.setActiveConnection(a); | ||||
| @@ -1254,8 +1493,7 @@ const NMVPNSection = new Lang.Class({ | ||||
|  | ||||
|     getIndicatorIcon: function() { | ||||
|         let items = this._connectionItems.values(); | ||||
|         for (let i = 0; i < items.length; i++) { | ||||
|             let item = items[i]; | ||||
|         for (let item of items) { | ||||
|             let icon = item.getIndicatorIcon(); | ||||
|             if (icon) | ||||
|                 return icon; | ||||
| @@ -1277,6 +1515,7 @@ const NMApplet = new Lang.Class({ | ||||
|  | ||||
|         // Device types | ||||
|         this._dtypes = { }; | ||||
|         this._dtypes[NetworkManager.DeviceType.ETHERNET] = NMDeviceWired; | ||||
|         this._dtypes[NetworkManager.DeviceType.WIFI] = NMDeviceWireless; | ||||
|         this._dtypes[NetworkManager.DeviceType.MODEM] = NMDeviceModem; | ||||
|         this._dtypes[NetworkManager.DeviceType.BT] = NMDeviceBluetooth; | ||||
| @@ -1284,6 +1523,7 @@ const NMApplet = new Lang.Class({ | ||||
|  | ||||
|         // Connection types | ||||
|         this._ctypes = { }; | ||||
|         this._ctypes[NetworkManager.SETTING_WIRED_SETTING_NAME] = NMConnectionCategory.WIRED; | ||||
|         this._ctypes[NetworkManager.SETTING_WIRELESS_SETTING_NAME] = NMConnectionCategory.WIRELESS; | ||||
|         this._ctypes[NetworkManager.SETTING_BLUETOOTH_SETTING_NAME] = NMConnectionCategory.WWAN; | ||||
|         this._ctypes[NetworkManager.SETTING_CDMA_SETTING_NAME] = NMConnectionCategory.WWAN; | ||||
| @@ -1306,6 +1546,15 @@ const NMApplet = new Lang.Class({ | ||||
|         this._tryLateInit(); | ||||
|     }, | ||||
|  | ||||
|     _createDeviceCategory: function() { | ||||
|         let category = { | ||||
|             section: new PopupMenu.PopupMenuSection(), | ||||
|             devices: [ ], | ||||
|         }; | ||||
|         this.menu.addMenuItem(category.section); | ||||
|         return category; | ||||
|     }, | ||||
|  | ||||
|     _tryLateInit: function() { | ||||
|         if (!this._client || !this._settings) | ||||
|             return; | ||||
| @@ -1321,17 +1570,9 @@ const NMApplet = new Lang.Class({ | ||||
|         this._nmDevices = []; | ||||
|         this._devices = { }; | ||||
|  | ||||
|         this._devices.wireless = { | ||||
|             section: new PopupMenu.PopupMenuSection(), | ||||
|             devices: [ ], | ||||
|         }; | ||||
|         this.menu.addMenuItem(this._devices.wireless.section); | ||||
|  | ||||
|         this._devices.wwan = { | ||||
|             section: new PopupMenu.PopupMenuSection(), | ||||
|             devices: [ ], | ||||
|         }; | ||||
|         this.menu.addMenuItem(this._devices.wwan.section); | ||||
|         this._devices.wired = this._createDeviceCategory(); | ||||
|         this._devices.wireless = this._createDeviceCategory(); | ||||
|         this._devices.wwan = this._createDeviceCategory(); | ||||
|  | ||||
|         this._vpnSection = new NMVPNSection(this._client); | ||||
|         this._vpnSection.connect('activation-failed', Lang.bind(this, this._onActivationFailed)); | ||||
| @@ -1575,7 +1816,7 @@ const NMApplet = new Lang.Class({ | ||||
|     _connectionRemoved: function(connection) { | ||||
|         let pos = this._connections.indexOf(connection); | ||||
|         if (pos != -1) | ||||
|             this._connections.splice(connection, 1); | ||||
|             this._connections.splice(pos, 1); | ||||
|  | ||||
|         let section = connection._section; | ||||
|  | ||||
| @@ -1601,7 +1842,6 @@ const NMApplet = new Lang.Class({ | ||||
|         let connectionSettings = connection.get_setting_by_name(NetworkManager.SETTING_CONNECTION_SETTING_NAME); | ||||
|         connection._type = connectionSettings.type; | ||||
|         connection._section = this._ctypes[connection._type] || NMConnectionCategory.INVALID; | ||||
|         connection._timestamp = connectionSettings.timestamp; | ||||
|  | ||||
|         let section = connection._section; | ||||
|  | ||||
| @@ -1626,8 +1866,7 @@ const NMApplet = new Lang.Class({ | ||||
|  | ||||
|     _updateIcon: function() { | ||||
|         if (!this._client.networking_enabled || !this._mainConnection) { | ||||
|             this._primaryIndicator.icon_name = 'network-offline-symbolic'; | ||||
|             this._primaryIndicator.visible = true; | ||||
|             this._primaryIndicator.visible = false; | ||||
|         } else { | ||||
|             let dev = this._mainConnection._primaryDevice; | ||||
|             this._primaryIndicator.visible = (dev != null); | ||||
|   | ||||
| @@ -2,7 +2,9 @@ | ||||
|  | ||||
| const Gio = imports.gi.Gio; | ||||
| const Lang = imports.lang; | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const Main = imports.ui.main; | ||||
| const PanelMenu = imports.ui.panelMenu; | ||||
| const PopupMenu = imports.ui.popupMenu; | ||||
|  | ||||
| @@ -12,11 +14,55 @@ const OBJECT_PATH = '/org/gnome/SettingsDaemon/Rfkill'; | ||||
| const RfkillManagerInterface = '<node> \ | ||||
| <interface name="org.gnome.SettingsDaemon.Rfkill"> \ | ||||
| <property name="AirplaneMode" type="b" access="readwrite" /> \ | ||||
| <property name="HardwareAirplaneMode" type="b" access="read" /> \ | ||||
| </interface> \ | ||||
| </node>'; | ||||
|  | ||||
| const RfkillManagerProxy = Gio.DBusProxy.makeProxyWrapper(RfkillManagerInterface); | ||||
|  | ||||
| const RfkillManager = new Lang.Class({ | ||||
|     Name: 'RfkillManager', | ||||
|  | ||||
|     _init: function() { | ||||
|         this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH, | ||||
|                                              Lang.bind(this, function(proxy, error) { | ||||
|                                                  if (error) { | ||||
|                                                      log(error.message); | ||||
|                                                      return; | ||||
|                                                  } | ||||
|                                                  this._proxy.connect('g-properties-changed', | ||||
|                                                                      Lang.bind(this, this._changed)); | ||||
|                                                  this._changed(); | ||||
|                                              })); | ||||
|     }, | ||||
|  | ||||
|     get airplaneMode() { | ||||
|         return this._proxy.AirplaneMode; | ||||
|     }, | ||||
|  | ||||
|     set airplaneMode(v) { | ||||
|         this._proxy.AirplaneMode = v; | ||||
|     }, | ||||
|  | ||||
|     get hwAirplaneMode() { | ||||
|         return this._proxy.HardwareAirplaneMode; | ||||
|     }, | ||||
|  | ||||
|     _changed: function() { | ||||
|         this.emit('airplane-mode-changed'); | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(RfkillManager.prototype); | ||||
|  | ||||
| var _manager; | ||||
| function getRfkillManager() { | ||||
|     if (_manager != null) | ||||
|         return _manager; | ||||
|  | ||||
|     _manager = new RfkillManager(); | ||||
|     return _manager; | ||||
| } | ||||
|  | ||||
| const Indicator = new Lang.Class({ | ||||
|     Name: 'RfkillIndicator', | ||||
|     Extends: PanelMenu.SystemIndicator, | ||||
| @@ -24,16 +70,8 @@ const Indicator = new Lang.Class({ | ||||
|     _init: function() { | ||||
|         this.parent(); | ||||
|  | ||||
|         this._proxy = new RfkillManagerProxy(Gio.DBus.session, BUS_NAME, OBJECT_PATH, | ||||
|                                              Lang.bind(this, function(proxy, error) { | ||||
|                                                  if (error) { | ||||
|                                                      log(error.message); | ||||
|                                                      return; | ||||
|                                                  } | ||||
|                                                  this._proxy.connect('g-properties-changed', | ||||
|                                                                      Lang.bind(this, this._sync)); | ||||
|                                                  this._sync(); | ||||
|                                              })); | ||||
|         this._manager = getRfkillManager(); | ||||
|         this._manager.connect('airplane-mode-changed', Lang.bind(this, this._sync)); | ||||
|  | ||||
|         this._indicator = this._addIndicator(); | ||||
|         this._indicator.icon_name = 'airplane-mode-symbolic'; | ||||
| @@ -45,16 +83,34 @@ const Indicator = new Lang.Class({ | ||||
|         this._item = new PopupMenu.PopupSubMenuMenuItem(_("Airplane Mode"), true); | ||||
|         this._item.icon.icon_name = 'airplane-mode-symbolic'; | ||||
|         this._item.status.text = _("On"); | ||||
|         this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() { | ||||
|             this._proxy.AirplaneMode = false; | ||||
|         this._offItem = this._item.menu.addAction(_("Turn Off"), Lang.bind(this, function() { | ||||
|             this._manager.airplaneMode = false; | ||||
|         })); | ||||
|         this._item.menu.addSettingsAction(_("Network Settings"), 'gnome-network-panel.desktop'); | ||||
|         this.menu.addMenuItem(this._item); | ||||
|  | ||||
|         Main.sessionMode.connect('updated', Lang.bind(this, this._sessionUpdated)); | ||||
|         this._sessionUpdated(); | ||||
|     }, | ||||
|  | ||||
|     _sessionUpdated: function() { | ||||
|         let sensitive = !Main.sessionMode.isLocked && !Main.sessionMode.isGreeter; | ||||
|         this.menu.setSensitive(sensitive); | ||||
|     }, | ||||
|  | ||||
|     _sync: function() { | ||||
|         let airplaneMode = this._proxy.AirplaneMode; | ||||
|         let airplaneMode = this._manager.airplaneMode; | ||||
|         let hwAirplaneMode = this._manager.hwAirplaneMode; | ||||
|         let changed = (airplaneMode != this._indicator.visible) || | ||||
|             (hwAirplaneMode != this._offItem.actor.visible); | ||||
|  | ||||
|         this._indicator.visible = airplaneMode; | ||||
|         this._item.actor.visible = airplaneMode; | ||||
|         this._offItem.setSensitive(!hwAirplaneMode); | ||||
|  | ||||
|         if (hwAirplaneMode) | ||||
|             this._offItem.label.text = _("Use hardware switch to turn off"); | ||||
|         else | ||||
|             this._offItem.label.text = _("Turn Off"); | ||||
|     }, | ||||
| }); | ||||
|   | ||||
| @@ -56,7 +56,10 @@ const AltSwitcher = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         if (this.actor.get_child() != childToShow) { | ||||
|             let hasFocus = this.actor.contains(global.stage.get_key_focus()); | ||||
|             this.actor.set_child(childToShow); | ||||
|             if (hasFocus) | ||||
|                 childToShow.grab_key_focus(); | ||||
|  | ||||
|             // The actors might respond to hover, so | ||||
|             // sync the pointer to make sure they update. | ||||
| @@ -81,7 +84,7 @@ const AltSwitcher = new Lang.Class({ | ||||
|                 this._sync(); | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| @@ -148,11 +151,11 @@ const Indicator = new Lang.Class({ | ||||
|         Gio.DBus.session.watch_name('org.gnome.SettingsDaemon.Orientation', | ||||
|                                     Gio.BusNameWatcherFlags.NONE, | ||||
|                                     Lang.bind(this, function() { | ||||
|                                         this._orentationExists = true; | ||||
|                                         this._orientationExists = true; | ||||
|                                         this._updateOrientationLock(); | ||||
|                                     }), | ||||
|                                     Lang.bind(this, function() { | ||||
|                                         this._orentationExists = false; | ||||
|                                         this._orientationExists = false; | ||||
|                                         this._updateOrientationLock(); | ||||
|                                     })); | ||||
|         this._updateOrientationLock(); | ||||
| @@ -282,7 +285,7 @@ const Indicator = new Lang.Class({ | ||||
|         let disabled = Main.sessionMode.isLocked || | ||||
|                        (Main.sessionMode.isGreeter && | ||||
|                         this._loginScreenSettings.get_boolean(DISABLE_RESTART_KEY)); | ||||
|         this._suspendAction.visible = this._haveShutdown && !disabled; | ||||
|         this._suspendAction.visible = this._haveSuspend && !disabled; | ||||
|         this._updateActionsVisibility(); | ||||
|     }, | ||||
|  | ||||
|   | ||||
| @@ -42,7 +42,7 @@ const StreamSlider = new Lang.Class({ | ||||
|         this.item.actor.add(this._icon); | ||||
|         this.item.actor.add(this._slider.actor, { expand: true }); | ||||
|         this.item.actor.connect('button-press-event', Lang.bind(this, function(actor, event) { | ||||
|             this._slider.startDragging(event); | ||||
|             return this._slider.startDragging(event); | ||||
|         })); | ||||
|         this.item.actor.connect('key-press-event', Lang.bind(this, function(actor, event) { | ||||
|             return this._slider.onKeyPressEvent(actor, event); | ||||
| @@ -94,7 +94,7 @@ const StreamSlider = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     scroll: function(event) { | ||||
|         this._slider.scroll(event); | ||||
|         return this._slider.scroll(event); | ||||
|     }, | ||||
|  | ||||
|     setValue: function(value) { | ||||
| @@ -276,7 +276,7 @@ const VolumeMenu = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     scroll: function(event) { | ||||
|         this._output.scroll(event); | ||||
|         return this._output.scroll(event); | ||||
|     }, | ||||
|  | ||||
|     _onControlStateChanged: function() { | ||||
| @@ -329,6 +329,6 @@ const Indicator = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _onScrollEvent: function(actor, event) { | ||||
|         this._volumeMenu.scroll(event); | ||||
|         return this._volumeMenu.scroll(event); | ||||
|     } | ||||
| }); | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| @@ -163,6 +164,7 @@ const SwitcherPopup = new Lang.Class({ | ||||
|                                                                Main.osdWindow.cancel(); | ||||
|                                                                this.actor.opacity = 255; | ||||
|                                                                this._initialDelayTimeoutId = 0; | ||||
|                                                                return GLib.SOURCE_REMOVE; | ||||
|                                                            })); | ||||
|         return true; | ||||
|     }, | ||||
| @@ -187,12 +189,13 @@ const SwitcherPopup = new Lang.Class({ | ||||
|  | ||||
|         this._disableHover(); | ||||
|  | ||||
|         if (this._keyPressHandler(keysym, backwards, action) != Clutter.EVENT_PROPAGATE) | ||||
|             return Clutter.EVENT_STOP; | ||||
|  | ||||
|         if (keysym == Clutter.Escape) | ||||
|             this.destroy(); | ||||
|         else | ||||
|             this._keyPressHandler(keysym, backwards, action); | ||||
|  | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _keyReleaseEvent: function(actor, event) { | ||||
| @@ -202,11 +205,12 @@ const SwitcherPopup = new Lang.Class({ | ||||
|         if (state == 0) | ||||
|             this._finish(event.get_time()); | ||||
|  | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _clickedOutside: function(actor, event) { | ||||
|         this.destroy(); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _scrollHandler: function(direction) { | ||||
| @@ -218,6 +222,7 @@ const SwitcherPopup = new Lang.Class({ | ||||
|  | ||||
|     _scrollEvent: function(actor, event) { | ||||
|         this._scrollHandler(event.get_scroll_direction()); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _itemActivatedHandler: function(n) { | ||||
| @@ -251,6 +256,7 @@ const SwitcherPopup = new Lang.Class({ | ||||
|     _mouseTimedOut: function() { | ||||
|         this._motionTimeoutId = 0; | ||||
|         this.mouseActive = true; | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     _popModal: function() { | ||||
| @@ -400,6 +406,7 @@ const SwitcherList = new Lang.Class({ | ||||
|  | ||||
|     _onItemEnter: function (index) { | ||||
|         this._itemEntered(index); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     highlight: function(index, justOutline) { | ||||
| @@ -446,10 +453,9 @@ const SwitcherList = new Lang.Class({ | ||||
|                            time: POPUP_SCROLL_TIME, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onComplete: Lang.bind(this, function () { | ||||
|                                 if (this._highlighted == 0) { | ||||
|                                 if (this._highlighted == 0) | ||||
|                                     this._scrollableLeft = false; | ||||
|                                     this.actor.queue_relayout(); | ||||
|                                 } | ||||
|                                 this.actor.queue_relayout(); | ||||
|                            }) | ||||
|                           }); | ||||
|     }, | ||||
| @@ -471,10 +477,9 @@ const SwitcherList = new Lang.Class({ | ||||
|                            time: POPUP_SCROLL_TIME, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onComplete: Lang.bind(this, function () { | ||||
|                                 if (this._highlighted == this._items.length - 1) { | ||||
|                                 if (this._highlighted == this._items.length - 1) | ||||
|                                     this._scrollableRight = false; | ||||
|                                     this.actor.queue_relayout(); | ||||
|                                 } | ||||
|                                 this.actor.queue_relayout(); | ||||
|                             }) | ||||
|                           }); | ||||
|     }, | ||||
| @@ -509,7 +514,7 @@ const SwitcherList = new Lang.Class({ | ||||
|     _getPreferredWidth: function (actor, forHeight, alloc) { | ||||
|         let [maxChildMin, maxChildNat] = this._maxChildWidth(forHeight); | ||||
|  | ||||
|         let totalSpacing = this._list.spacing * (this._items.length - 1); | ||||
|         let totalSpacing = Math.max(this._list.spacing * (this._items.length - 1), 0); | ||||
|         alloc.min_size = this._items.length * maxChildMin + totalSpacing; | ||||
|         alloc.natural_size = alloc.min_size; | ||||
|         this._minSize = alloc.min_size; | ||||
| @@ -539,7 +544,7 @@ const SwitcherList = new Lang.Class({ | ||||
|         let childHeight = box.y2 - box.y1; | ||||
|  | ||||
|         let [maxChildMin, maxChildNat] = this._maxChildWidth(childHeight); | ||||
|         let totalSpacing = this._list.spacing * (this._items.length - 1); | ||||
|         let totalSpacing = Math.max(this._list.spacing * (this._items.length - 1), 0); | ||||
|  | ||||
|         let childWidth = Math.floor(Math.max(0, box.x2 - box.x1 - totalSpacing) / this._items.length); | ||||
|  | ||||
|   | ||||
| @@ -71,7 +71,7 @@ const UnlockDialog = new Lang.Class({ | ||||
|                                                     child: otherUserLabel, | ||||
|                                                     reactive: true, | ||||
|                                                     x_align: St.Align.START, | ||||
|                                                     x_fill: true }); | ||||
|                                                     x_fill: false }); | ||||
|             this._otherUserButton.connect('clicked', Lang.bind(this, this._otherUserClicked)); | ||||
|             this._promptBox.add_child(this._otherUserButton); | ||||
|         } else { | ||||
|   | ||||
| @@ -79,9 +79,16 @@ const UserWidgetLabel = new Lang.Class({ | ||||
|         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(); | ||||
|  | ||||
|         // We can't override the destroy vfunc because that might be called during | ||||
|         // object finalization, and we can't call any JS inside a GC finalize callback, | ||||
|         // so we use a signal, that will be disconnected by GObject the first time | ||||
|         // the actor is destroyed (which is guaranteed to be as part of a normal | ||||
|         // destroy() call from JS, possibly from some ancestor) | ||||
|         this.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|     }, | ||||
|  | ||||
|     vfunc_destroy: function() { | ||||
|     _onDestroy: function() { | ||||
|         if (this._userLoadedId != 0) { | ||||
|             this._user.disconnect(this._userLoadedId); | ||||
|             this._userLoadedId = 0; | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Gtk = imports.gi.Gtk; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Meta = imports.gi.Meta; | ||||
| @@ -157,17 +158,14 @@ const ViewSelector = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     show: function() { | ||||
|         this._activePage = this._workspacesPage; | ||||
|  | ||||
|         this.reset(); | ||||
|         this._appsPage.hide(); | ||||
|         this._searchPage.hide(); | ||||
|  | ||||
|         this._workspacesDisplay.show(); | ||||
|         this._activePage = null; | ||||
|         this._showPage(this._workspacesPage); | ||||
|  | ||||
|         if (!this._workspacesDisplay.activeWorkspaceHasMaximizedWindows()) | ||||
|             Main.overview.fadeOutDesktop(); | ||||
|  | ||||
|         this._showPage(this._workspacesPage, true); | ||||
|     }, | ||||
|  | ||||
|     zoomFromOverview: function() { | ||||
| @@ -203,6 +201,7 @@ const ViewSelector = new Lang.Class({ | ||||
|                                                       this._a11yFocusPage(page); | ||||
|                                                   }) | ||||
|                                             });; | ||||
|         page.hide(); | ||||
|         this.actor.add_actor(page); | ||||
|         return page; | ||||
|     }, | ||||
| @@ -268,7 +267,7 @@ const ViewSelector = new Lang.Class({ | ||||
|         // Ignore events while anything but the overview has | ||||
|         // pushed a modal (system modals, looking glass, ...) | ||||
|         if (Main.modalCount > 1) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         let modifiers = event.get_state(); | ||||
|         let symbol = event.get_key_symbol(); | ||||
| @@ -280,10 +279,10 @@ const ViewSelector = new Lang.Class({ | ||||
|                 this._showAppsButton.checked = false; | ||||
|             else | ||||
|                 Main.overview.hide(); | ||||
|             return true; | ||||
|             return Clutter.EVENT_STOP; | ||||
|         } else if (this._shouldTriggerSearch(symbol)) { | ||||
|             this.startSearch(event); | ||||
|         } else if (!this._searchActive) { | ||||
|         } else if (!this._searchActive && !global.stage.key_focus) { | ||||
|             if (symbol == Clutter.Tab || symbol == Clutter.Down) { | ||||
|                 this._activePage.navigate_focus(null, Gtk.DirectionType.TAB_FORWARD, false); | ||||
|                 return true; | ||||
| @@ -292,7 +291,7 @@ const ViewSelector = new Lang.Class({ | ||||
|                 return true; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _searchCancelled: function() { | ||||
| @@ -408,7 +407,7 @@ const ViewSelector = new Lang.Class({ | ||||
|         if (symbol == Clutter.Escape) { | ||||
|             if (this._isActivated()) { | ||||
|                 this.reset(); | ||||
|                 return true; | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } | ||||
|         } else if (this._searchActive) { | ||||
|             let arrowNext, nextDirection; | ||||
| @@ -422,18 +421,18 @@ const ViewSelector = new Lang.Class({ | ||||
|  | ||||
|             if (symbol == Clutter.Tab) { | ||||
|                 this._searchResults.navigateFocus(Gtk.DirectionType.TAB_FORWARD); | ||||
|                 return true; | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } else if (symbol == Clutter.ISO_Left_Tab) { | ||||
|                 this._focusTrap.can_focus = false; | ||||
|                 this._searchResults.navigateFocus(Gtk.DirectionType.TAB_BACKWARD); | ||||
|                 this._focusTrap.can_focus = true; | ||||
|                 return true; | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } else if (symbol == Clutter.Down) { | ||||
|                 this._searchResults.navigateFocus(Gtk.DirectionType.DOWN); | ||||
|                 return true; | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } else if (symbol == arrowNext && this._text.position == -1) { | ||||
|                 this._searchResults.navigateFocus(nextDirection); | ||||
|                 return true; | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } else if (symbol == Clutter.Return || symbol == Clutter.KP_Enter) { | ||||
|                 // We can't connect to 'activate' here because search providers | ||||
|                 // might want to do something with the modifiers in activateDefault. | ||||
| @@ -442,10 +441,10 @@ const ViewSelector = new Lang.Class({ | ||||
|                     this._doSearch(); | ||||
|                 } | ||||
|                 this._searchResults.activateDefault(); | ||||
|                 return true; | ||||
|                 return Clutter.EVENT_STOP; | ||||
|             } | ||||
|         } | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onCapturedEvent: function(actor, event) { | ||||
| @@ -460,7 +459,7 @@ const ViewSelector = new Lang.Class({ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _doSearch: function () { | ||||
| @@ -470,6 +469,8 @@ const ViewSelector = new Lang.Class({ | ||||
|  | ||||
|         this._searchResults.setTerms(terms); | ||||
|         this._showPage(this._searchPage); | ||||
|  | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     getActivePage: function() { | ||||
| @@ -481,13 +482,6 @@ const ViewSelector = new Lang.Class({ | ||||
|             return ViewPage.SEARCH; | ||||
|     }, | ||||
|  | ||||
|     setActivePage: function(page) { | ||||
|         if (page == ViewPage.WINDOWS) | ||||
|             this._showPage(this._workspacesPage); | ||||
|         else | ||||
|             this._showPage(this._appsPage); | ||||
|     }, | ||||
|  | ||||
|     fadeIn: function() { | ||||
|         let actor = this._activePage; | ||||
|         Tweener.addTween(actor, { opacity: 255, | ||||
|   | ||||
| @@ -16,7 +16,7 @@ const WindowAttentionHandler = new Lang.Class({ | ||||
|  | ||||
|     _getTitleAndBanner: function(app, window) { | ||||
|         let title = app.get_name(); | ||||
|         let banner = _("'%s' is ready").format(window.get_title()); | ||||
|         let banner = _("“%s” is ready").format(window.get_title()); | ||||
|         return [title, banner] | ||||
|     }, | ||||
|  | ||||
|   | ||||
| @@ -106,11 +106,11 @@ const DisplayChangeDialog = new Lang.Class({ | ||||
|             /* mutter already takes care of failing at timeout */ | ||||
|             this._timeoutId = 0; | ||||
|             this.close(); | ||||
|             return false; | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         } | ||||
|  | ||||
|         this._descriptionLabel.text = this._formatCountDown(); | ||||
|         return true; | ||||
|         return GLib.SOURCE_CONTINUE; | ||||
|     }, | ||||
|  | ||||
|     _onFailure: function() { | ||||
| @@ -191,6 +191,7 @@ const WorkspaceTracker = new Lang.Class({ | ||||
|         tracker.connect('startup-sequence-changed', Lang.bind(this, this._queueCheckWorkspaces)); | ||||
|  | ||||
|         global.screen.connect('notify::n-workspaces', Lang.bind(this, this._nWorkspacesChanged)); | ||||
|         global.window_manager.connect('switch-workspace', Lang.bind(this, this._queueCheckWorkspaces)); | ||||
|  | ||||
|         global.screen.connect('window-entered-monitor', Lang.bind(this, this._windowEnteredMonitor)); | ||||
|         global.screen.connect('window-left-monitor', Lang.bind(this, this._windowLeftMonitor)); | ||||
| @@ -232,12 +233,13 @@ const WorkspaceTracker = new Lang.Class({ | ||||
|  | ||||
|         let windows = global.get_window_actors(); | ||||
|         for (i = 0; i < windows.length; i++) { | ||||
|             let win = windows[i]; | ||||
|             let actor = windows[i]; | ||||
|             let win = actor.get_meta_window(); | ||||
|  | ||||
|             if (win.get_meta_window().is_on_all_workspaces()) | ||||
|             if (win.is_on_all_workspaces()) | ||||
|                 continue; | ||||
|  | ||||
|             let workspaceIndex = win.get_workspace(); | ||||
|             let workspaceIndex = win.get_workspace().index(); | ||||
|             emptyWorkspaces[workspaceIndex] = false; | ||||
|         } | ||||
|  | ||||
| @@ -248,13 +250,7 @@ const WorkspaceTracker = new Lang.Class({ | ||||
|         } | ||||
|  | ||||
|         let activeWorkspaceIndex = global.screen.get_active_workspace_index(); | ||||
|         let removingCurrentWorkspace = (emptyWorkspaces[activeWorkspaceIndex] && | ||||
|                                         activeWorkspaceIndex < emptyWorkspaces.length - 1); | ||||
|  | ||||
|         if (removingCurrentWorkspace) { | ||||
|             // "Merge" the empty workspace we are removing with the one at the end | ||||
|             this._wm.blockAnimations(); | ||||
|         } | ||||
|         emptyWorkspaces[activeWorkspaceIndex] = false; | ||||
|  | ||||
|         // Delete other empty workspaces; do it from the end to avoid index changes | ||||
|         for (i = emptyWorkspaces.length - 2; i >= 0; i--) { | ||||
| @@ -262,11 +258,6 @@ const WorkspaceTracker = new Lang.Class({ | ||||
|                 global.screen.remove_workspace(this._workspaces[i], global.get_current_time()); | ||||
|         } | ||||
|  | ||||
|         if (removingCurrentWorkspace) { | ||||
|             global.screen.get_workspace_by_index(global.screen.n_workspaces - 1).activate(global.get_current_time()); | ||||
|             this._wm.unblockAnimations(); | ||||
|         } | ||||
|  | ||||
|         this._checkWorkspacesId = 0; | ||||
|         return false; | ||||
|     }, | ||||
| @@ -278,7 +269,7 @@ const WorkspaceTracker = new Lang.Class({ | ||||
|         workspace._keepAliveId = Mainloop.timeout_add(duration, Lang.bind(this, function() { | ||||
|             workspace._keepAliveId = 0; | ||||
|             this._queueCheckWorkspaces(); | ||||
|             return false; | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
| @@ -290,7 +281,7 @@ const WorkspaceTracker = new Lang.Class({ | ||||
|                 workspace._lastRemovedWindow = null; | ||||
|                 this._queueCheckWorkspaces(); | ||||
|             } | ||||
|             return false; | ||||
|             return GLib.SOURCE_REMOVE; | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
| @@ -367,6 +358,93 @@ const WorkspaceTracker = new Lang.Class({ | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const TilePreview = new Lang.Class({ | ||||
|     Name: 'TilePreview', | ||||
|  | ||||
|     _init: function() { | ||||
|         this.actor = new St.Widget(); | ||||
|         global.window_group.add_actor(this.actor); | ||||
|  | ||||
|         this._reset(); | ||||
|         this._showing = false; | ||||
|     }, | ||||
|  | ||||
|     show: function(window, tileRect, monitorIndex) { | ||||
|         let windowActor = window.get_compositor_private(); | ||||
|         if (!windowActor) | ||||
|             return; | ||||
|  | ||||
|         global.window_group.set_child_below_sibling(this.actor, windowActor); | ||||
|  | ||||
|         if (this._rect && this._rect.equal(tileRect)) | ||||
|             return; | ||||
|  | ||||
|         let changeMonitor = (this._monitorIndex == -1 || | ||||
|                              this._monitorIndex != monitorIndex); | ||||
|  | ||||
|         this._monitorIndex = monitorIndex; | ||||
|         this._rect = tileRect; | ||||
|  | ||||
|         let monitor = Main.layoutManager.monitors[monitorIndex]; | ||||
|  | ||||
|         this._updateStyle(monitor); | ||||
|  | ||||
|         if (!this._showing || changeMonitor) { | ||||
|             let monitorRect = new Meta.Rectangle({ x: monitor.x, | ||||
|                                                    y: monitor.y, | ||||
|                                                    width: monitor.width, | ||||
|                                                    height: monitor.height }); | ||||
|             let [, rect] = window.get_outer_rect().intersect(monitorRect); | ||||
|             this.actor.set_size(rect.width, rect.height); | ||||
|             this.actor.set_position(rect.x, rect.y); | ||||
|             this.actor.opacity = 0; | ||||
|         } | ||||
|  | ||||
|         this._showing = true; | ||||
|         this.actor.show(); | ||||
|         Tweener.addTween(this.actor, | ||||
|                          { x: tileRect.x, | ||||
|                            y: tileRect.y, | ||||
|                            width: tileRect.width, | ||||
|                            height: tileRect.height, | ||||
|                            opacity: 255, | ||||
|                            time: WINDOW_ANIMATION_TIME, | ||||
|                            transition: 'easeOutQuad' | ||||
|                          }); | ||||
|     }, | ||||
|  | ||||
|     hide: function() { | ||||
|         if (!this._showing) | ||||
|             return; | ||||
|  | ||||
|         this._showing = false; | ||||
|         Tweener.addTween(this.actor, | ||||
|                          { opacity: 0, | ||||
|                            time: WINDOW_ANIMATION_TIME, | ||||
|                            transition: 'easeOutQuad', | ||||
|                            onComplete: Lang.bind(this, this._reset) | ||||
|                          }); | ||||
|     }, | ||||
|  | ||||
|     _reset: function() { | ||||
|         this.actor.hide(); | ||||
|         this._rect = null; | ||||
|         this._monitorIndex = -1; | ||||
|     }, | ||||
|  | ||||
|     _updateStyle: function(monitor) { | ||||
|         let styles = ['tile-preview']; | ||||
|         if (this._monitorIndex == Main.layoutManager.primaryIndex) | ||||
|             styles.push('on-primary'); | ||||
|         if (this._rect.x == monitor.x) | ||||
|             styles.push('tile-preview-left'); | ||||
|         if (this._rect.x + this._rect.width == monitor.x + monitor.width) | ||||
|             styles.push('tile-preview-right'); | ||||
|  | ||||
|         this.actor.style_class = styles.join(' '); | ||||
|     } | ||||
| }); | ||||
|  | ||||
| const WindowManager = new Lang.Class({ | ||||
|     Name: 'WindowManager', | ||||
|  | ||||
| @@ -397,6 +475,8 @@ const WindowManager = new Lang.Class({ | ||||
|         })); | ||||
|  | ||||
|         this._shellwm.connect('switch-workspace', Lang.bind(this, this._switchWorkspace)); | ||||
|         this._shellwm.connect('show-tile-preview', Lang.bind(this, this._showTilePreview)); | ||||
|         this._shellwm.connect('hide-tile-preview', Lang.bind(this, this._hideTilePreview)); | ||||
|         this._shellwm.connect('minimize', Lang.bind(this, this._minimizeWindow)); | ||||
|         this._shellwm.connect('maximize', Lang.bind(this, this._maximizeWindow)); | ||||
|         this._shellwm.connect('unmaximize', Lang.bind(this, this._unmaximizeWindow)); | ||||
| @@ -406,6 +486,8 @@ const WindowManager = new Lang.Class({ | ||||
|         this._shellwm.connect('confirm-display-change', Lang.bind(this, this._confirmDisplayChange)); | ||||
|  | ||||
|         this._workspaceSwitcherPopup = null; | ||||
|         this._tilePreview = null; | ||||
|  | ||||
|         this.setCustomKeybindingHandler('switch-to-workspace-left', | ||||
|                                         Shell.KeyBindingMode.NORMAL | | ||||
|                                         Shell.KeyBindingMode.OVERVIEW, | ||||
| @@ -555,6 +637,12 @@ const WindowManager = new Lang.Class({ | ||||
|                                         Shell.KeyBindingMode.LOGIN_SCREEN, | ||||
|                                         Lang.bind(this, this._startA11ySwitcher)); | ||||
|  | ||||
|         this.addKeybinding('pause-resume-tweens', | ||||
|                            new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }), | ||||
|                            Meta.KeyBindingFlags.NONE, | ||||
|                            Shell.KeyBindingMode.ALL, | ||||
|                            Lang.bind(this, this._toggleTweens)); | ||||
|  | ||||
|         this.addKeybinding('open-application-menu', | ||||
|                            new Gio.Settings({ schema: SHELL_KEYBINDINGS_SCHEMA }), | ||||
|                            Meta.KeyBindingFlags.NONE, | ||||
| @@ -742,15 +830,13 @@ const WindowManager = new Lang.Class({ | ||||
|         if (shouldDim && !window._dimmed) { | ||||
|             window._dimmed = true; | ||||
|             this._dimmedWindows.push(window); | ||||
|             if (!Main.overview.visible) | ||||
|                 this._dimWindow(window); | ||||
|             this._dimWindow(window); | ||||
|         } else if (!shouldDim && window._dimmed) { | ||||
|             window._dimmed = false; | ||||
|             this._dimmedWindows = this._dimmedWindows.filter(function(win) { | ||||
|                                                                  return win != window; | ||||
|                                                              }); | ||||
|             if (!Main.overview.visible) | ||||
|                 this._undimWindow(window); | ||||
|             this._undimWindow(window); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
| @@ -975,25 +1061,29 @@ const WindowManager = new Lang.Class({ | ||||
|         wgroup.add_actor(switchData.movingWindowBin); | ||||
|  | ||||
|         for (let i = 0; i < windows.length; i++) { | ||||
|             let window = windows[i]; | ||||
|             let actor = windows[i]; | ||||
|             let window = actor.get_meta_window(); | ||||
|  | ||||
|             if (!window.meta_window.showing_on_its_workspace()) | ||||
|             if (!window.showing_on_its_workspace()) | ||||
|                 continue; | ||||
|  | ||||
|             if (this._movingWindow && window.meta_window == this._movingWindow) { | ||||
|                 switchData.movingWindow = { window: window, | ||||
|                                             parent: window.get_parent() }; | ||||
|             if (window.is_on_all_workspaces()) | ||||
|                 continue; | ||||
|  | ||||
|             let record = { window: actor, | ||||
|                            parent: actor.get_parent() }; | ||||
|  | ||||
|             if (this._movingWindow && window == this._movingWindow) { | ||||
|                 switchData.movingWindow = record; | ||||
|                 switchData.windows.push(switchData.movingWindow); | ||||
|                 window.reparent(switchData.movingWindowBin); | ||||
|             } else if (window.get_workspace() == from) { | ||||
|                 switchData.windows.push({ window: window, | ||||
|                                           parent: window.get_parent() }); | ||||
|                 window.reparent(switchData.outGroup); | ||||
|             } else if (window.get_workspace() == to) { | ||||
|                 switchData.windows.push({ window: window, | ||||
|                                           parent: window.get_parent() }); | ||||
|                 window.reparent(switchData.inGroup); | ||||
|                 window.show(); | ||||
|                 actor.reparent(switchData.movingWindowBin); | ||||
|             } else if (window.get_workspace().index() == from) { | ||||
|                 switchData.windows.push(record); | ||||
|                 actor.reparent(switchData.outGroup); | ||||
|             } else if (window.get_workspace().index() == to) { | ||||
|                 switchData.windows.push(record); | ||||
|                 actor.reparent(switchData.inGroup); | ||||
|                 actor.show(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @@ -1047,6 +1137,18 @@ const WindowManager = new Lang.Class({ | ||||
|         shellwm.completed_switch_workspace(); | ||||
|     }, | ||||
|  | ||||
|     _showTilePreview: function(shellwm, window, tileRect, monitorIndex) { | ||||
|         if (!this._tilePreview) | ||||
|             this._tilePreview = new TilePreview(); | ||||
|         this._tilePreview.show(window, tileRect, monitorIndex); | ||||
|     }, | ||||
|  | ||||
|     _hideTilePreview: function(shellwm) { | ||||
|         if (!this._tilePreview) | ||||
|             return; | ||||
|         this._tilePreview.hide(); | ||||
|     }, | ||||
|  | ||||
|     _startAppSwitcher : function(display, screen, window, binding) { | ||||
|         /* prevent a corner case where both popups show up at once */ | ||||
|         if (this._workspaceSwitcherPopup != null) | ||||
| @@ -1083,6 +1185,15 @@ const WindowManager = new Lang.Class({ | ||||
|         Main.panel.toggleAppMenu(); | ||||
|     }, | ||||
|  | ||||
|     _toggleTweens: function() { | ||||
|         this._tweensPaused = !this._tweensPaused; | ||||
|         const OrigTweener = imports.tweener.tweener; | ||||
|         if (this._tweensPaused) | ||||
|             OrigTweener.pauseAllTweens(); | ||||
|         else | ||||
|             OrigTweener.resumeAllTweens(); | ||||
|     }, | ||||
|  | ||||
|     _showWorkspaceSwitcher : function(display, screen, window, binding) { | ||||
|         if (!Main.sessionMode.hasWorkspaces) | ||||
|             return; | ||||
|   | ||||
| @@ -1,18 +1,22 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Meta = imports.gi.Meta; | ||||
| const Pango = imports.gi.Pango; | ||||
| const Shell = imports.gi.Shell; | ||||
| const St = imports.gi.St; | ||||
| const Atk = imports.gi.Atk; | ||||
|  | ||||
| const Signals = imports.signals; | ||||
|  | ||||
| const DND = imports.ui.dnd; | ||||
| const Main = imports.ui.main; | ||||
| const Overview = imports.ui.overview; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const WindowManager = imports.ui.windowManager; | ||||
|  | ||||
| const WINDOW_DND_SIZE = 256; | ||||
|  | ||||
| @@ -34,6 +38,68 @@ function _interpolate(start, end, step) { | ||||
|     return start + (end - start) * step; | ||||
| } | ||||
|  | ||||
| const WindowCloneLayout = new Lang.Class({ | ||||
|     Name: 'WindowCloneLayout', | ||||
|     Extends: Clutter.LayoutManager, | ||||
|  | ||||
|     _init: function(boundingBox) { | ||||
|         this.parent(); | ||||
|  | ||||
|         this._boundingBox = boundingBox; | ||||
|     }, | ||||
|  | ||||
|     get boundingBox() { | ||||
|         return this._boundingBox; | ||||
|     }, | ||||
|  | ||||
|     set boundingBox(b) { | ||||
|         this._boundingBox = b; | ||||
|         this.layout_changed(); | ||||
|     }, | ||||
|  | ||||
|     _makeBoxForWindow: function(window) { | ||||
|         // We need to adjust the position of the actor because of the | ||||
|         // consequences of invisible borders -- in reality, the texture | ||||
|         // has an extra set of "padding" around it that we need to trim | ||||
|         // down. | ||||
|  | ||||
|         // The outer rect (from which we compute the bounding box) | ||||
|         // paradoxically is the smaller rectangle, containing the positions | ||||
|         // of the visible frame. The input rect contains everything, | ||||
|         // including the invisible border padding. | ||||
|         let inputRect = window.get_input_rect(); | ||||
|  | ||||
|         let box = new Clutter.ActorBox(); | ||||
|  | ||||
|         box.set_origin(inputRect.x - this._boundingBox.x, | ||||
|                        inputRect.y - this._boundingBox.y); | ||||
|         box.set_size(inputRect.width, inputRect.height); | ||||
|  | ||||
|         return box; | ||||
|     }, | ||||
|  | ||||
|     vfunc_get_preferred_height: function(container, forWidth) { | ||||
|         return [this._boundingBox.height, this._boundingBox.height]; | ||||
|     }, | ||||
|  | ||||
|     vfunc_get_preferred_width: function(container, forHeight) { | ||||
|         return [this._boundingBox.width, this._boundingBox.width]; | ||||
|     }, | ||||
|  | ||||
|     vfunc_allocate: function(container, box, flags) { | ||||
|         let clone = container.get_children().forEach(function (child) { | ||||
|             let realWindow; | ||||
|             if (child == container._delegate._windowClone) | ||||
|                 realWindow = container._delegate.realWindow; | ||||
|             else | ||||
|                 realWindow = child.source; | ||||
|  | ||||
|             child.allocate(this._makeBoxForWindow(realWindow.meta_window), | ||||
|                            flags); | ||||
|         }, this); | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| const WindowClone = new Lang.Class({ | ||||
|     Name: 'WindowClone', | ||||
|  | ||||
| @@ -43,10 +109,7 @@ const WindowClone = new Lang.Class({ | ||||
|         this.metaWindow._delegate = this; | ||||
|         this._workspace = workspace; | ||||
|  | ||||
|         let [borderX, borderY] = this._getInvisibleBorderPadding(); | ||||
|         this._windowClone = new Clutter.Clone({ source: realWindow.get_texture(), | ||||
|                                                 x: -borderX, | ||||
|                                                 y: -borderY }); | ||||
|         this._windowClone = new Clutter.Clone({ source: realWindow.get_texture() }); | ||||
|         // We expect this.actor to be used for all interaction rather than | ||||
|         // this._windowClone; as the former is reactive and the latter | ||||
|         // is not, this just works for most cases. However, for DND all | ||||
| @@ -54,20 +117,14 @@ const WindowClone = new Lang.Class({ | ||||
|         // To avoid this, we hide it from pick. | ||||
|         Shell.util_set_hidden_from_pick(this._windowClone, true); | ||||
|  | ||||
|         this.origX = realWindow.x + borderX; | ||||
|         this.origY = realWindow.y + borderY; | ||||
|  | ||||
|         let outerRect = realWindow.meta_window.get_outer_rect(); | ||||
|  | ||||
|         // The MetaShapedTexture that we clone has a size that includes | ||||
|         // the invisible border; this is inconvenient; rather than trying | ||||
|         // to compensate all over the place we insert a ClutterActor into | ||||
|         // the hierarchy that is sized to only the visible portion. | ||||
|         this.actor = new Clutter.Actor({ reactive: true, | ||||
|                                          x: this.origX, | ||||
|                                          y: this.origY, | ||||
|                                          width: outerRect.width, | ||||
|                                          height: outerRect.height }); | ||||
|         this.actor = new St.Widget({ reactive: true, | ||||
|                                      can_focus: true, | ||||
|                                      accessible_role: Atk.Role.PUSH_BUTTON, | ||||
|                                      layout_manager: new WindowCloneLayout() }); | ||||
|  | ||||
|         this.actor.add_child(this._windowClone); | ||||
|  | ||||
| @@ -77,18 +134,27 @@ const WindowClone = new Lang.Class({ | ||||
|         this._dragSlot = [0, 0, 0, 0]; | ||||
|         this._stackAbove = null; | ||||
|  | ||||
|         this._sizeChangedId = this.realWindow.connect('size-changed', | ||||
|         this._windowClone._updateId = this.metaWindow.connect('size-changed', | ||||
|             Lang.bind(this, this._onRealWindowSizeChanged)); | ||||
|         this._realWindowDestroyId = this.realWindow.connect('destroy', | ||||
|             Lang.bind(this, this._disconnectRealWindowSignals)); | ||||
|         this._windowClone._destroyId = this.realWindow.connect('destroy', Lang.bind(this, function() { | ||||
|             // First destroy the clone and then destroy everything | ||||
|             // This will ensure that we never see it in the _disconnectSignals loop | ||||
|             this._windowClone.destroy(); | ||||
|             this.destroy(); | ||||
|         })); | ||||
|  | ||||
|         this._updateAttachedDialogs(); | ||||
|         this._computeBoundingBox(); | ||||
|         this.actor.x = this._boundingBox.x; | ||||
|         this.actor.y = this._boundingBox.y; | ||||
|  | ||||
|         let clickAction = new Clutter.ClickAction(); | ||||
|         clickAction.connect('clicked', Lang.bind(this, this._onClicked)); | ||||
|         clickAction.connect('long-press', Lang.bind(this, this._onLongPress)); | ||||
|  | ||||
|         this.actor.add_action(clickAction); | ||||
|  | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|         this.actor.connect('key-press-event', Lang.bind(this, this._onKeyPress)); | ||||
|         this.actor.connect('enter-event', Lang.bind(this, this._onEnter)); | ||||
|  | ||||
|         this._draggable = DND.makeDraggable(this.actor, | ||||
|                                             { restoreOnSuccess: true, | ||||
| @@ -114,6 +180,83 @@ const WindowClone = new Lang.Class({ | ||||
|             return this._slot; | ||||
|     }, | ||||
|  | ||||
|     deleteAll: function() { | ||||
|         // Delete all windows, starting from the bottom-most (most-modal) one | ||||
|  | ||||
|         let windows = this.actor.get_children(); | ||||
|         for (let i = windows.length - 1; i >= 1; i--) { | ||||
|             let realWindow = windows[i].source; | ||||
|             let metaWindow = realWindow.meta_window; | ||||
|  | ||||
|             metaWindow.delete(global.get_current_time()); | ||||
|         } | ||||
|  | ||||
|         this.metaWindow.delete(global.get_current_time()); | ||||
|     }, | ||||
|  | ||||
|     addAttachedDialog: function(win) { | ||||
|         this._doAddAttachedDialog(win, win.get_compositor_private()); | ||||
|         this._computeBoundingBox(); | ||||
|         this.emit('size-changed'); | ||||
|     }, | ||||
|  | ||||
|     _doAddAttachedDialog: function(metaWin, realWin) { | ||||
|         let clone = new Clutter.Clone({ source: realWin }); | ||||
|         clone._updateId = metaWin.connect('size-changed', Lang.bind(this, function() { | ||||
|             this._computeBoundingBox(); | ||||
|             this.emit('size-changed'); | ||||
|         })); | ||||
|         clone._destroyId = realWin.connect('destroy', Lang.bind(this, function() { | ||||
|             clone.destroy(); | ||||
|  | ||||
|             this._computeBoundingBox(); | ||||
|             this.emit('size-changed'); | ||||
|         })); | ||||
|         this.actor.add_child(clone); | ||||
|     }, | ||||
|  | ||||
|     _updateAttachedDialogs: function() { | ||||
|         let iter = Lang.bind(this, function(win) { | ||||
|             let actor = win.get_compositor_private(); | ||||
|  | ||||
|             if (!actor) | ||||
|                 return false; | ||||
|             if (!win.is_attached_dialog()) | ||||
|                 return false; | ||||
|  | ||||
|             this._doAddAttachedDialog(win, actor); | ||||
|             win.foreach_transient(iter); | ||||
|             return true; | ||||
|         }); | ||||
|         this.metaWindow.foreach_transient(iter); | ||||
|     }, | ||||
|  | ||||
|     get boundingBox() { | ||||
|         return this._boundingBox; | ||||
|     }, | ||||
|  | ||||
|     getOriginalPosition: function() { | ||||
|         return [this._boundingBox.x, this._boundingBox.y]; | ||||
|     }, | ||||
|  | ||||
|     _computeBoundingBox: function() { | ||||
|         let rect = this.metaWindow.get_outer_rect(); | ||||
|  | ||||
|         this.actor.get_children().forEach(function (child) { | ||||
|             let realWindow; | ||||
|             if (child == this._windowClone) | ||||
|                 realWindow = this.realWindow; | ||||
|             else | ||||
|                 realWindow = child.source; | ||||
|  | ||||
|             let metaWindow = realWindow.meta_window; | ||||
|             rect = rect.union(metaWindow.get_outer_rect()); | ||||
|         }, this); | ||||
|  | ||||
|         this._boundingBox = rect; | ||||
|         this.actor.layout_manager.boundingBox = rect; | ||||
|     }, | ||||
|  | ||||
|     // Find the actor just below us, respecting reparenting done | ||||
|     // by DND code | ||||
|     getActualStackAbove: function() { | ||||
| @@ -147,44 +290,26 @@ const WindowClone = new Lang.Class({ | ||||
|         this.actor.destroy(); | ||||
|     }, | ||||
|  | ||||
|     _disconnectRealWindowSignals: function() { | ||||
|         if (this._sizeChangedId > 0) | ||||
|             this.realWindow.disconnect(this._sizeChangedId); | ||||
|         this._sizeChangedId = 0; | ||||
|     _disconnectSignals: function() { | ||||
|         this.actor.get_children().forEach(Lang.bind(this, function (child) { | ||||
|             let realWindow; | ||||
|             if (child == this._windowClone) | ||||
|                 realWindow = this.realWindow; | ||||
|             else | ||||
|                 realWindow = child.source; | ||||
|  | ||||
|         if (this._realWindowDestroyId > 0) | ||||
|             this.realWindow.disconnect(this._realWindowDestroyId); | ||||
|         this._realWindowDestroyId = 0; | ||||
|     }, | ||||
|  | ||||
|     _getInvisibleBorderPadding: function() { | ||||
|         // We need to adjust the position of the actor because of the | ||||
|         // consequences of invisible borders -- in reality, the texture | ||||
|         // has an extra set of "padding" around it that we need to trim | ||||
|         // down. | ||||
|  | ||||
|         // The outer rect paradoxically is the smaller rectangle, | ||||
|         // containing the positions of the visible frame. The input | ||||
|         // rect contains everything, including the invisible border | ||||
|         // padding. | ||||
|         let outerRect = this.metaWindow.get_outer_rect(); | ||||
|         let inputRect = this.metaWindow.get_input_rect(); | ||||
|         let [borderX, borderY] = [outerRect.x - inputRect.x, | ||||
|                                   outerRect.y - inputRect.y]; | ||||
|  | ||||
|         return [borderX, borderY]; | ||||
|             realWindow.meta_window.disconnect(child._updateId); | ||||
|             realWindow.disconnect(child._destroyId); | ||||
|         })); | ||||
|     }, | ||||
|  | ||||
|     _onRealWindowSizeChanged: function() { | ||||
|         let [borderX, borderY] = this._getInvisibleBorderPadding(); | ||||
|         let outerRect = this.metaWindow.get_outer_rect(); | ||||
|         this.actor.set_size(outerRect.width, outerRect.height); | ||||
|         this._windowClone.set_position(-borderX, -borderY); | ||||
|         this._computeBoundingBox(); | ||||
|         this.emit('size-changed'); | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
|         this._disconnectRealWindowSignals(); | ||||
|         this._disconnectSignals(); | ||||
|  | ||||
|         this.metaWindow._delegate = null; | ||||
|         this.actor._delegate = null; | ||||
| @@ -197,11 +322,30 @@ const WindowClone = new Lang.Class({ | ||||
|         this.disconnectAll(); | ||||
|     }, | ||||
|  | ||||
|     _onClicked: function(action, actor) { | ||||
|     _activate: function() { | ||||
|         this._selected = true; | ||||
|         this.emit('selected', global.get_current_time()); | ||||
|     }, | ||||
|  | ||||
|     _onKeyPress: function(actor, event) { | ||||
|         let symbol = event.get_key_symbol(); | ||||
|         let isEnter = (symbol == Clutter.KEY_Return || symbol == Clutter.KEY_KP_Enter); | ||||
|         if (isEnter) { | ||||
|             this._activate(); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     _onEnter: function() { | ||||
|         this.actor.grab_key_focus(); | ||||
|     }, | ||||
|  | ||||
|     _onClicked: function(action, actor) { | ||||
|         this._activate(); | ||||
|     }, | ||||
|  | ||||
|     _onLongPress: function(action, actor, state) { | ||||
|         // Take advantage of the Clutter policy to consider | ||||
|         // a long-press canceled when the pointer movement | ||||
| @@ -283,6 +427,7 @@ const WindowOverlay = new Lang.Class({ | ||||
|                                    text: metaWindow.title }); | ||||
|         title.clutter_text.ellipsize = Pango.EllipsizeMode.END; | ||||
|         title._spacing = 0; | ||||
|         windowClone.actor.label_actor = title; | ||||
|  | ||||
|         this._updateCaptionId = metaWindow.connect('notify::title', | ||||
|             Lang.bind(this, function(w) { | ||||
| @@ -301,6 +446,10 @@ const WindowOverlay = new Lang.Class({ | ||||
|                                   Lang.bind(this, this._onEnter)); | ||||
|         windowClone.actor.connect('leave-event', | ||||
|                                   Lang.bind(this, this._onLeave)); | ||||
|         windowClone.actor.connect('key-focus-in', | ||||
|                                   Lang.bind(this, this._onEnter)); | ||||
|         windowClone.actor.connect('key-focus-out', | ||||
|                                   Lang.bind(this, this._onLeave)); | ||||
|  | ||||
|         this._windowAddedId = 0; | ||||
|  | ||||
| @@ -429,7 +578,7 @@ const WindowOverlay = new Lang.Class({ | ||||
|                                                       Lang.bind(this, | ||||
|                                                                 this._onWindowAdded)); | ||||
|  | ||||
|         metaWindow.delete(global.get_current_time()); | ||||
|         this._windowClone.deleteAll(); | ||||
|     }, | ||||
|  | ||||
|     _windowCanClose: function() { | ||||
| @@ -448,7 +597,7 @@ const WindowOverlay = new Lang.Class({ | ||||
|             Mainloop.idle_add(Lang.bind(this, | ||||
|                                         function() { | ||||
|                                             this._windowClone.emit('selected'); | ||||
|                                             return false; | ||||
|                                             return GLib.SOURCE_REMOVE; | ||||
|                                         })); | ||||
|         } | ||||
|     }, | ||||
| @@ -512,15 +661,17 @@ const WindowOverlay = new Lang.Class({ | ||||
|         // as the close button will be shown as needed when the overlays | ||||
|         // are shown again | ||||
|         if (this._hidden) | ||||
|             return; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|  | ||||
|         this._animateVisible(); | ||||
|         this.emit('show-close-button'); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _onLeave: function() { | ||||
|         if (this._idleToggleCloseId == 0) | ||||
|             this._idleToggleCloseId = Mainloop.timeout_add(750, Lang.bind(this, this._idleToggleCloseButton)); | ||||
|         return Clutter.EVENT_PROPAGATE; | ||||
|     }, | ||||
|  | ||||
|     _idleToggleCloseButton: function() { | ||||
| @@ -530,7 +681,7 @@ const WindowOverlay = new Lang.Class({ | ||||
|             !this.closeButton.has_pointer) | ||||
|             this._animateInvisible(); | ||||
|  | ||||
|         return false; | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     hideCloseButton: function() { | ||||
| @@ -1198,18 +1349,18 @@ const Workspace = new Lang.Class({ | ||||
|             // store current cursor position | ||||
|             this._cursorX = x; | ||||
|             this._cursorY = y; | ||||
|             return true; | ||||
|             return GLib.SOURCE_CONTINUE; | ||||
|         } | ||||
|  | ||||
|         let actorUnderPointer = global.stage.get_actor_at_pos(Clutter.PickMode.REACTIVE, x, y); | ||||
|         for (let i = 0; i < this._windows.length; i++) { | ||||
|             if (this._windows[i].actor == actorUnderPointer) | ||||
|                 return true; | ||||
|                 return GLib.SOURCE_CONTINUE; | ||||
|         } | ||||
|  | ||||
|         this._recalculateWindowPositions(WindowPositionFlags.ANIMATE); | ||||
|         this._repositionWindowsId = 0; | ||||
|         return false; | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     _doRemoveWindow : function(metaWin) { | ||||
| @@ -1284,7 +1435,7 @@ const Workspace = new Lang.Class({ | ||||
|                                                 metaWin.get_compositor_private() && | ||||
|                                                 metaWin.get_workspace() == this.metaWorkspace) | ||||
|                                                 this._doAddWindow(metaWin); | ||||
|                                             return false; | ||||
|                                             return GLib.SOURCE_REMOVE; | ||||
|                                         })); | ||||
|             return; | ||||
|         } | ||||
| @@ -1294,9 +1445,29 @@ const Workspace = new Lang.Class({ | ||||
|         if (this._lookupIndex (metaWin) != -1) | ||||
|             return; | ||||
|  | ||||
|         if (!this._isMyWindow(win) || !this._isOverviewWindow(win)) | ||||
|         if (!this._isMyWindow(win)) | ||||
|             return; | ||||
|  | ||||
|         if (!this._isOverviewWindow(win)) { | ||||
|             if (metaWin.is_attached_dialog()) { | ||||
|                 let parent = metaWin.get_transient_for(); | ||||
|                 while (parent.is_attached_dialog()) | ||||
|                     parent = metaWin.get_transient_for(); | ||||
|  | ||||
|                 let idx = this._lookupIndex (parent); | ||||
|                 if (idx < 0) { | ||||
|                     // parent was not created yet, it will take care | ||||
|                     // of the dialog when created | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 let clone = this._windows[idx]; | ||||
|                 clone.addAttachedDialog(metaWin); | ||||
|             } | ||||
|  | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         let [clone, overlay] = this._addWindowClone(win, false); | ||||
|  | ||||
|         if (win._overviewHint) { | ||||
| @@ -1384,9 +1555,11 @@ const Workspace = new Lang.Class({ | ||||
|                 overlay.hide(); | ||||
|  | ||||
|             if (clone.metaWindow.showing_on_its_workspace()) { | ||||
|                 let [origX, origY] = clone.getOriginalPosition(); | ||||
|  | ||||
|                 Tweener.addTween(clone.actor, | ||||
|                                  { x: clone.origX, | ||||
|                                    y: clone.origY, | ||||
|                                  { x: origX, | ||||
|                                    y: origY, | ||||
|                                    scale_x: 1.0, | ||||
|                                    scale_y: 1.0, | ||||
|                                    time: Overview.ANIMATION_TIME, | ||||
| @@ -1450,8 +1623,7 @@ const Workspace = new Lang.Class({ | ||||
|  | ||||
|     // Tests if @win should be shown in the Overview | ||||
|     _isOverviewWindow : function (win) { | ||||
|         let tracker = Shell.WindowTracker.get_default(); | ||||
|         return tracker.is_window_interesting(win.get_meta_window()); | ||||
|         return !win.get_meta_window().skip_taskbar; | ||||
|     }, | ||||
|  | ||||
|     // Create a clone of a (non-desktop) window and add it to the window list | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| // -*- mode: js; js-indent-level: 4; indent-tabs-mode: nil -*- | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Meta  = imports.gi.Meta; | ||||
| @@ -156,6 +157,7 @@ const WorkspaceSwitcherPopup = new Lang.Class({ | ||||
|                                             onComplete: function() { this.destroy(); }, | ||||
|                                             onCompleteScope: this | ||||
|                                            }); | ||||
|         return GLib.SOURCE_REMOVE; | ||||
|     }, | ||||
|  | ||||
|     destroy: function() { | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
|  | ||||
| const Clutter = imports.gi.Clutter; | ||||
| const Gio = imports.gi.Gio; | ||||
| const GLib = imports.gi.GLib; | ||||
| const Lang = imports.lang; | ||||
| const Mainloop = imports.mainloop; | ||||
| const Meta = imports.gi.Meta; | ||||
| @@ -13,6 +14,7 @@ const Background = imports.ui.background; | ||||
| const DND = imports.ui.dnd; | ||||
| const Main = imports.ui.main; | ||||
| const Tweener = imports.ui.tweener; | ||||
| const WindowManager = imports.ui.windowManager; | ||||
| const Workspace = imports.ui.workspace; | ||||
| const WorkspacesView = imports.ui.workspacesView; | ||||
|  | ||||
| @@ -31,20 +33,49 @@ const WORKSPACE_KEEP_ALIVE_TIME = 100; | ||||
|  | ||||
| const OVERRIDE_SCHEMA = 'org.gnome.shell.overrides'; | ||||
|  | ||||
| /* A layout manager that requests size only for primary_actor, but then allocates | ||||
|    all using a fixed layout */ | ||||
| const PrimaryActorLayout = new Lang.Class({ | ||||
|     Name: 'PrimaryActorLayout', | ||||
|     Extends: Clutter.FixedLayout, | ||||
|  | ||||
|     _init: function(primaryActor) { | ||||
|         this.parent(); | ||||
|  | ||||
|         this.primaryActor = primaryActor; | ||||
|     }, | ||||
|  | ||||
|     vfunc_get_preferred_width: function(forHeight) { | ||||
|         return this.primaryActor.get_preferred_width(forHeight); | ||||
|     }, | ||||
|  | ||||
|     vfunc_get_preferred_height: function(forWidth) { | ||||
|         return this.primaryActor.get_preferred_height(forWidth); | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| const WindowClone = new Lang.Class({ | ||||
|     Name: 'WindowClone', | ||||
|  | ||||
|     _init : function(realWindow) { | ||||
|         this.actor = new Clutter.Clone({ source: realWindow.get_texture(), | ||||
|         this.clone = new Clutter.Clone({ source: realWindow }); | ||||
|  | ||||
|         /* Can't use a Shell.GenericContainer because of DND and reparenting... */ | ||||
|         this.actor = new Clutter.Actor({ layout_manager: new PrimaryActorLayout(this.clone), | ||||
|                                          reactive: true }); | ||||
|         this.actor._delegate = this; | ||||
|         this.actor.add_child(this.clone); | ||||
|         this.realWindow = realWindow; | ||||
|         this.metaWindow = realWindow.meta_window; | ||||
|  | ||||
|         this._positionChangedId = this.realWindow.connect('position-changed', | ||||
|                                                           Lang.bind(this, this._onPositionChanged)); | ||||
|         this._realWindowDestroyedId = this.realWindow.connect('destroy', | ||||
|                                                               Lang.bind(this, this._disconnectRealWindowSignals)); | ||||
|         this.clone._updateId = this.metaWindow.connect('position-changed', | ||||
|                                                        Lang.bind(this, this._onPositionChanged)); | ||||
|         this.clone._destroyId = this.realWindow.connect('destroy', Lang.bind(this, function() { | ||||
|             // First destroy the clone and then destroy everything | ||||
|             // This will ensure that we never see it in the _disconnectSignals loop | ||||
|             this.clone.destroy(); | ||||
|             this.destroy(); | ||||
|         })); | ||||
|         this._onPositionChanged(); | ||||
|  | ||||
|         this.actor.connect('button-release-event', | ||||
| @@ -60,6 +91,21 @@ const WindowClone = new Lang.Class({ | ||||
|         this._draggable.connect('drag-cancelled', Lang.bind(this, this._onDragCancelled)); | ||||
|         this._draggable.connect('drag-end', Lang.bind(this, this._onDragEnd)); | ||||
|         this.inDrag = false; | ||||
|  | ||||
|         let iter = Lang.bind(this, function(win) { | ||||
|             let actor = win.get_compositor_private(); | ||||
|  | ||||
|             if (!actor) | ||||
|                 return false; | ||||
|             if (!win.is_attached_dialog()) | ||||
|                 return false; | ||||
|  | ||||
|             this._doAddAttachedDialog(win, actor); | ||||
|             win.foreach_transient(iter); | ||||
|  | ||||
|             return true; | ||||
|         }); | ||||
|         this.metaWindow.foreach_transient(iter); | ||||
|     }, | ||||
|  | ||||
|     // Find the actor just below us, respecting reparenting done | ||||
| @@ -97,25 +143,46 @@ const WindowClone = new Lang.Class({ | ||||
|         this.actor.destroy(); | ||||
|     }, | ||||
|  | ||||
|     addAttachedDialog: function(win) { | ||||
|         this._doAddAttachedDialog(win, win.get_compositor_private()); | ||||
|     }, | ||||
|  | ||||
|     _doAddAttachedDialog: function(metaDialog, realDialog) { | ||||
|         let clone = new Clutter.Clone({ source: realDialog }); | ||||
|         this._updateDialogPosition(realDialog, clone); | ||||
|  | ||||
|         clone._updateId = metaDialog.connect('position-changed', | ||||
|                                              Lang.bind(this, this._updateDialogPosition, clone)); | ||||
|         clone._destroyId = realDialog.connect('destroy', Lang.bind(this, function() { | ||||
|             clone.destroy(); | ||||
|         })); | ||||
|         this.actor.add_child(clone); | ||||
|     }, | ||||
|  | ||||
|     _updateDialogPosition: function(realDialog, cloneDialog) { | ||||
|         let metaDialog = realDialog.meta_window; | ||||
|         let dialogRect = metaDialog.get_outer_rect(); | ||||
|         let rect = this.metaWindow.get_outer_rect(); | ||||
|  | ||||
|         cloneDialog.set_position(dialogRect.x - rect.x, dialogRect.y - rect.y); | ||||
|     }, | ||||
|  | ||||
|     _onPositionChanged: function() { | ||||
|         let rect = this.metaWindow.get_outer_rect(); | ||||
|         this.actor.set_position(this.realWindow.x, this.realWindow.y); | ||||
|     }, | ||||
|  | ||||
|     _disconnectRealWindowSignals: function() { | ||||
|         if (this._positionChangedId != 0) { | ||||
|             this.realWindow.disconnect(this._positionChangedId); | ||||
|             this._positionChangedId = 0; | ||||
|         } | ||||
|     _disconnectSignals: function() { | ||||
|         this.actor.get_children().forEach(function(child) { | ||||
|             let realWindow = child.source; | ||||
|  | ||||
|         if (this._realWindowDestroyedId != 0) { | ||||
|             this.realWindow.disconnect(this._realWindowDestroyedId); | ||||
|             this._realWindowDestroyedId = 0; | ||||
|         } | ||||
|             realWindow.meta_window.disconnect(child._updateId); | ||||
|             realWindow.disconnect(child._destroyId); | ||||
|         }); | ||||
|     }, | ||||
|  | ||||
|     _onDestroy: function() { | ||||
|         this._disconnectRealWindowSignals(); | ||||
|         this._disconnectSignals(); | ||||
|  | ||||
|         this.actor._delegate = null; | ||||
|  | ||||
| @@ -130,7 +197,7 @@ const WindowClone = new Lang.Class({ | ||||
|     _onButtonRelease : function (actor, event) { | ||||
|         this.emit('selected', event.get_time()); | ||||
|  | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _onDragBegin : function (draggable, time) { | ||||
| @@ -325,7 +392,7 @@ const WorkspaceThumbnail = new Lang.Class({ | ||||
|                                                 metaWin.get_compositor_private() && | ||||
|                                                 metaWin.get_workspace() == this.metaWorkspace) | ||||
|                                                 this._doAddWindow(metaWin); | ||||
|                                             return false; | ||||
|                                             return GLib.SOURCE_REMOVE; | ||||
|                                         })); | ||||
|             return; | ||||
|         } | ||||
| @@ -343,10 +410,26 @@ const WorkspaceThumbnail = new Lang.Class({ | ||||
|         if (this._lookupIndex (metaWin) != -1) | ||||
|             return; | ||||
|  | ||||
|         if (!this._isMyWindow(win) || !this._isOverviewWindow(win)) | ||||
|         if (!this._isMyWindow(win)) | ||||
|             return; | ||||
|  | ||||
|         let clone = this._addWindowClone(win); | ||||
|         if (this._isOverviewWindow(win)) { | ||||
|             this._addWindowClone(win); | ||||
|         } else if (metaWin.is_attached_dialog()) { | ||||
|             let parent = metaWin.get_transient_for(); | ||||
|             while (parent.is_attached_dialog()) | ||||
|                 parent = metaWin.get_transient_for(); | ||||
|  | ||||
|             let idx = this._lookupIndex (parent); | ||||
|             if (idx < 0) { | ||||
|                 // parent was not created yet, it will take care | ||||
|                 // of the dialog when created | ||||
|                 return; | ||||
|             } | ||||
|  | ||||
|             let clone = this._windows[idx]; | ||||
|             clone.addAttachedDialog(metaWin); | ||||
|         } | ||||
|     }, | ||||
|  | ||||
|     _windowAdded : function(metaWorkspace, metaWin) { | ||||
| @@ -424,8 +507,7 @@ const WorkspaceThumbnail = new Lang.Class({ | ||||
|  | ||||
|     // Tests if @win should be shown in the Overview | ||||
|     _isOverviewWindow : function (win) { | ||||
|         let tracker = Shell.WindowTracker.get_default(); | ||||
|         return tracker.is_window_interesting(win.get_meta_window()) && | ||||
|         return !win.get_meta_window().skip_taskbar && | ||||
|                win.get_meta_window().showing_on_its_workspace(); | ||||
|     }, | ||||
|  | ||||
| @@ -561,7 +643,7 @@ const ThumbnailsBox = new Lang.Class({ | ||||
|  | ||||
|         this._thumbnails = []; | ||||
|  | ||||
|         this.actor.connect('button-press-event', function() { return true; }); | ||||
|         this.actor.connect('button-press-event', function() { return Clutter.EVENT_STOP; }); | ||||
|         this.actor.connect('button-release-event', Lang.bind(this, this._onButtonRelease)); | ||||
|  | ||||
|         Main.overview.connect('showing', | ||||
| @@ -606,7 +688,7 @@ const ThumbnailsBox = new Lang.Class({ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     }, | ||||
|  | ||||
|     _onDragBegin: function() { | ||||
| @@ -722,22 +804,25 @@ const ThumbnailsBox = new Lang.Class({ | ||||
|             [newWorkspaceIndex, this._dropPlaceholderPos] = [this._dropPlaceholderPos, -1]; | ||||
|  | ||||
|             // Nab all the windows below us. | ||||
|             let windows = global.get_window_actors().filter(function(win) { | ||||
|             let windows = global.get_window_actors().filter(function(winActor) { | ||||
|                 // If the window is attached to an ancestor, we don't need/want to move it | ||||
|                 if (!!win.meta_window.get_transient_for()) | ||||
|                 let window = winActor.meta_window; | ||||
|  | ||||
|                 if (window.get_transient_for() != null) | ||||
|                     return false; | ||||
|  | ||||
|                 if (isWindow) | ||||
|                     return win.get_workspace() >= newWorkspaceIndex && win != source; | ||||
|                     return window.get_workspace().index() >= newWorkspaceIndex && winActor != source; | ||||
|                 else | ||||
|                     return win.get_workspace() >= newWorkspaceIndex; | ||||
|                     return window.get_workspace().index() >= newWorkspaceIndex; | ||||
|             }); | ||||
|  | ||||
|             this._spliceIndex = newWorkspaceIndex; | ||||
|  | ||||
|             // ... move them down one. | ||||
|             windows.forEach(function(win) { | ||||
|                 win.meta_window.change_workspace_by_index(win.get_workspace() + 1, true); | ||||
|             windows.forEach(function(winActor) { | ||||
|                 let window = winActor.meta_window; | ||||
|                 window.change_workspace_by_index(window.get_workspace().index() + 1, true); | ||||
|             }); | ||||
|  | ||||
|             if (isWindow) | ||||
| @@ -817,7 +902,10 @@ const ThumbnailsBox = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _workspacesChanged: function() { | ||||
|         let oldNumWorkspaces = this._thumbnails.length; | ||||
|         let validThumbnails = this._thumbnails.filter(function(t) { | ||||
|             return t.state <= ThumbnailState.NORMAL; | ||||
|         }); | ||||
|         let oldNumWorkspaces = validThumbnails.length; | ||||
|         let newNumWorkspaces = global.screen.n_workspaces; | ||||
|         let active = global.screen.get_active_workspace_index(); | ||||
|  | ||||
|   | ||||
| @@ -30,6 +30,7 @@ const WorkspacesViewBase = new Lang.Class({ | ||||
|         this.actor = new St.Widget({ style_class: 'workspaces-view', | ||||
|                                      reactive: true }); | ||||
|         this.actor.connect('destroy', Lang.bind(this, this._onDestroy)); | ||||
|         global.focus_manager.add_group(this.actor); | ||||
|  | ||||
|         // The actor itself isn't a drop target, so we don't want to pick on its area | ||||
|         this.actor.set_size(0, 0); | ||||
| @@ -236,29 +237,32 @@ const WorkspacesView = new Lang.Class({ | ||||
|     }, | ||||
|  | ||||
|     _updateWorkspaces: function() { | ||||
|         let oldNumWorkspaces = this._workspaces.length; | ||||
|         let newNumWorkspaces = global.screen.n_workspaces; | ||||
|  | ||||
|         this.scrollAdjustment.upper = newNumWorkspaces; | ||||
|  | ||||
|         if (newNumWorkspaces > oldNumWorkspaces) { | ||||
|             for (let w = oldNumWorkspaces; w < newNumWorkspaces; w++) { | ||||
|                 let metaWorkspace = global.screen.get_workspace_by_index(w); | ||||
|                 let workspace = new Workspace.Workspace(metaWorkspace, this._monitorIndex); | ||||
|                 this._workspaces.push(workspace); | ||||
|                 this.actor.add_actor(workspace.actor); | ||||
|             } | ||||
|         let needsUpdate = false; | ||||
|         for (let j = 0; j < newNumWorkspaces; j++) { | ||||
|             let metaWorkspace = global.screen.get_workspace_by_index(j); | ||||
|             let workspace; | ||||
|  | ||||
|             if (this._fullGeometry) | ||||
|                 this._updateWorkspaceActors(false); | ||||
|         } else if (newNumWorkspaces < oldNumWorkspaces) { | ||||
|             let nRemoved = (newNumWorkspaces - oldNumWorkspaces); | ||||
|             let removed = this._workspaces.splice(oldNumWorkspaces, nRemoved); | ||||
|             removed.forEach(function(workspace) { | ||||
|                 workspace.destroy(); | ||||
|             }); | ||||
|             if (j >= this._workspaces.length) { /* added */ | ||||
|                 workspace = new Workspace.Workspace(metaWorkspace, this._monitorIndex); | ||||
|                 this.actor.add_actor(workspace.actor); | ||||
|                 this._workspaces[j] = workspace; | ||||
|             } else  { | ||||
|                 workspace = this._workspaces[j]; | ||||
|  | ||||
|                 if (workspace.metaWorkspace != metaWorkspace) { /* removed */ | ||||
|                     workspace.destroy(); | ||||
|                     this._workspaces.splice(j, 1); | ||||
|                 } /* else kept */ | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (this._fullGeometry) | ||||
|             this._updateWorkspaceActors(false); | ||||
|  | ||||
|         this._syncGeometry(); | ||||
|     }, | ||||
|  | ||||
| @@ -371,11 +375,21 @@ const ExtraWorkspaceView = new Lang.Class({ | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| const DelegateFocusNavigator = new Lang.Class({ | ||||
|     Name: 'DelegateFocusNavigator', | ||||
|     Extends: St.Widget, | ||||
|  | ||||
|     vfunc_navigate_focus: function(from, direction) { | ||||
|         return this._delegate.navigateFocus(from, direction); | ||||
|     }, | ||||
| }); | ||||
|  | ||||
| const WorkspacesDisplay = new Lang.Class({ | ||||
|     Name: 'WorkspacesDisplay', | ||||
|  | ||||
|     _init: function() { | ||||
|         this.actor = new St.Widget({ clip_to_allocation: true }); | ||||
|         this.actor = new DelegateFocusNavigator({ clip_to_allocation: true }); | ||||
|         this.actor._delegate = this; | ||||
|         this.actor.connect('notify::allocation', Lang.bind(this, this._updateWorkspacesActualGeometry)); | ||||
|         this.actor.connect('parent-set', Lang.bind(this, this._parentSet)); | ||||
|  | ||||
| @@ -391,7 +405,7 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|         Main.overview.addAction(clickAction); | ||||
|         this.actor.bind_property('mapped', clickAction, 'enabled', GObject.BindingFlags.SYNC_CREATE); | ||||
|  | ||||
|         let panAction = new Clutter.PanAction(); | ||||
|         let panAction = new Clutter.PanAction({ threshold_trigger_edge: Clutter.GestureTriggerEdge.AFTER }); | ||||
|         panAction.connect('pan', Lang.bind(this, this._onPan)); | ||||
|         panAction.connect('gesture-begin', Lang.bind(this, function() { | ||||
|             for (let i = 0; i < this._workspacesViews.length; i++) | ||||
| @@ -399,7 +413,6 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|             return true; | ||||
|         })); | ||||
|         panAction.connect('gesture-cancel', Lang.bind(this, function() { | ||||
|             clickAction.release(); | ||||
|             for (let i = 0; i < this._workspacesViews.length; i++) | ||||
|                 this._workspacesViews[i].endSwipeScroll(); | ||||
|         })); | ||||
| @@ -437,6 +450,10 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|         return false; | ||||
|     }, | ||||
|  | ||||
|     navigateFocus: function(from, direction) { | ||||
|         return this._getPrimaryView().actor.navigate_focus(from, direction, false); | ||||
|     }, | ||||
|  | ||||
|     show: function() { | ||||
|         this._updateWorkspacesViews(); | ||||
|         for (let i = 0; i < this._workspacesViews.length; i++) | ||||
| @@ -599,7 +616,7 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|  | ||||
|     _onScrollEvent: function(actor, event) { | ||||
|         if (!this.actor.mapped) | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         let activeWs = global.screen.get_active_workspace(); | ||||
|         let ws; | ||||
|         switch (event.get_scroll_direction()) { | ||||
| @@ -610,10 +627,10 @@ const WorkspacesDisplay = new Lang.Class({ | ||||
|             ws = activeWs.get_neighbor(Meta.MotionDirection.DOWN); | ||||
|             break; | ||||
|         default: | ||||
|             return false; | ||||
|             return Clutter.EVENT_PROPAGATE; | ||||
|         } | ||||
|         Main.wm.actionMoveWorkspace(ws); | ||||
|         return true; | ||||
|         return Clutter.EVENT_STOP; | ||||
|     } | ||||
| }); | ||||
| Signals.addSignalMethods(WorkspacesDisplay.prototype); | ||||
|   | ||||
| @@ -62,7 +62,7 @@ const XdndHandler = new Lang.Class({ | ||||
|             let cursorWindow = windows[windows.length - 1]; | ||||
|  | ||||
|             // FIXME: more reliable way? | ||||
|             if (!cursorWindow.is_override_redirect()) | ||||
|             if (!cursorWindow.get_meta_window().is_override_redirect()) | ||||
|                 return; | ||||
|  | ||||
|             let constraint_position = new Clutter.BindConstraint({ coordinate : Clutter.BindCoordinate.POSITION, | ||||
|   | ||||
| @@ -30,9 +30,11 @@ hi | ||||
| hu | ||||
| ia | ||||
| id | ||||
| is | ||||
| it | ||||
| ja | ||||
| kk | ||||
| km | ||||
| kn | ||||
| ko | ||||
| ku | ||||
| @@ -46,6 +48,7 @@ ms | ||||
| nb | ||||
| nl | ||||
| nn | ||||
| oc | ||||
| or | ||||
| pa | ||||
| pl | ||||
|   | ||||
| @@ -45,6 +45,7 @@ js/ui/status/accessibility.js | ||||
| js/ui/status/bluetooth.js | ||||
| js/ui/status/brightness.js | ||||
| js/ui/status/keyboard.js | ||||
| js/ui/status/location.js | ||||
| js/ui/status/network.js | ||||
| js/ui/status/power.js | ||||
| js/ui/status/rfkill.js | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user